diff options
Diffstat (limited to 'libraries')
435 files changed, 37701 insertions, 0 deletions
diff --git a/libraries/ActionBarSherlock/.gitignore b/libraries/ActionBarSherlock/.gitignore new file mode 100644 index 000000000..71c11b159 --- /dev/null +++ b/libraries/ActionBarSherlock/.gitignore @@ -0,0 +1,30 @@ +#Android specific +bin +gen +obj +libs/armeabi +lint.xml +local.properties +release.properties +ant.properties +*.class +*.apk + +#Gradle +.gradle +build +gradle.properties + +#Maven +target +pom.xml.* + +#Eclipse +.project +.classpath +.settings +.metadata + +#IntelliJ IDEA +.idea +*.iml diff --git a/libraries/ActionBarSherlock/AndroidManifest.xml b/libraries/ActionBarSherlock/AndroidManifest.xml new file mode 100644 index 000000000..7b8a84824 --- /dev/null +++ b/libraries/ActionBarSherlock/AndroidManifest.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="100" android:versionName="4.2.0" package="com.actionbarsherlock"> + +    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="16"/> + +</manifest> diff --git a/libraries/ActionBarSherlock/README.md b/libraries/ActionBarSherlock/README.md new file mode 100644 index 000000000..e8a2c080e --- /dev/null +++ b/libraries/ActionBarSherlock/README.md @@ -0,0 +1,15 @@ +ActionBarSherlock Library +========================= + +This folder contains the main library which should be linked against as an +Android library project in your application. + +For more information see the "Including In Your Project" section of the +[usage page][1]. + + + + + + + [1]: http://actionbarsherlock.com/usage.html diff --git a/libraries/ActionBarSherlock/build.gradle b/libraries/ActionBarSherlock/build.gradle new file mode 100644 index 000000000..102d66946 --- /dev/null +++ b/libraries/ActionBarSherlock/build.gradle @@ -0,0 +1,27 @@ +buildscript { +    repositories { +        mavenCentral() +    } +    dependencies { +        classpath 'com.android.tools.build:gradle:0.4.1' +    } +} + +apply plugin: 'android-library' + +dependencies { +    compile files('libs/android-support-v4.jar') +} + +android { +    compileSdkVersion 17 +    buildToolsVersion '17' + +    sourceSets { +        main { +            manifest.srcFile 'AndroidManifest.xml' +            java.srcDirs = ['src'] +            res.srcDirs = ['res'] +        } +    } +} diff --git a/libraries/ActionBarSherlock/build.xml b/libraries/ActionBarSherlock/build.xml new file mode 100644 index 000000000..a10a91491 --- /dev/null +++ b/libraries/ActionBarSherlock/build.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="MainActivity" default="help"> + +    <!-- The local.properties file is created and updated by the 'android' tool. +         It contains the path to the SDK. It should *NOT* be checked into +         Version Control Systems. --> +    <property file="local.properties" /> + +    <!-- The ant.properties file can be created by you. It is only edited by the +         'android' tool to add properties to it. +         This is the place to change some Ant specific build properties. +         Here are some properties you may want to change/update: + +         source.dir +             The name of the source directory. Default is 'src'. +         out.dir +             The name of the output directory. Default is 'bin'. + +         For other overridable properties, look at the beginning of the rules +         files in the SDK, at tools/ant/build.xml + +         Properties related to the SDK location or the project target should +         be updated using the 'android' tool with the 'update' action. + +         This file is an integral part of the build system for your +         application and should be checked into Version Control Systems. + +         --> +    <property file="ant.properties" /> + +    <!-- if sdk.dir was not set from one of the property file, then +         get it from the ANDROID_HOME env var. +         This must be done before we load project.properties since +         the proguard config can use sdk.dir --> +    <property environment="env" /> +    <condition property="sdk.dir" value="${env.ANDROID_HOME}"> +        <isset property="env.ANDROID_HOME" /> +    </condition> + +    <!-- The project.properties file is created and updated by the 'android' +         tool, as well as ADT. + +         This contains project specific properties such as project target, and library +         dependencies. Lower level build properties are stored in ant.properties +         (or in .classpath for Eclipse projects). + +         This file is an integral part of the build system for your +         application and should be checked into Version Control Systems. --> +    <loadproperties srcFile="project.properties" /> + +    <!-- quick check on sdk.dir --> +    <fail +            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable." +            unless="sdk.dir" +    /> + +    <!-- +        Import per project custom build rules if present at the root of the project. +        This is the place to put custom intermediary targets such as: +            -pre-build +            -pre-compile +            -post-compile (This is typically used for code obfuscation. +                           Compiled code location: ${out.classes.absolute.dir} +                           If this is not done in place, override ${out.dex.input.absolute.dir}) +            -post-package +            -post-build +            -pre-clean +    --> +    <import file="custom_rules.xml" optional="true" /> + +    <!-- Import the actual build file. + +         To customize existing targets, there are two options: +         - Customize only one target: +             - copy/paste the target into this file, *before* the +               <import> task. +             - customize it to your needs. +         - Customize the whole content of build.xml +             - copy/paste the content of the rules files (minus the top node) +               into this file, replacing the <import> task. +             - customize to your needs. + +         *********************** +         ****** IMPORTANT ****** +         *********************** +         In all cases you must update the value of version-tag below to read 'custom' instead of an integer, +         in order to avoid having your file be overridden by tools such as "android update project" +    --> +    <!-- version-tag: 1 --> +    <import file="${sdk.dir}/tools/ant/build.xml" /> + +</project> diff --git a/libraries/ActionBarSherlock/libs/android-support-v4.jar b/libraries/ActionBarSherlock/libs/android-support-v4.jar Binary files differnew file mode 100644 index 000000000..99e063b33 --- /dev/null +++ b/libraries/ActionBarSherlock/libs/android-support-v4.jar diff --git a/libraries/ActionBarSherlock/pom.xml b/libraries/ActionBarSherlock/pom.xml new file mode 100644 index 000000000..3b6ce40ce --- /dev/null +++ b/libraries/ActionBarSherlock/pom.xml @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> +	<modelVersion>4.0.0</modelVersion> + +	<artifactId>actionbarsherlock</artifactId> +	<name>ActionBarSherlock</name> +	<packaging>apklib</packaging> + +	<parent> +		<groupId>com.actionbarsherlock</groupId> +		<artifactId>parent</artifactId> +		<version>4.2.0</version> +		<relativePath>../pom.xml</relativePath> +	</parent> + +	<dependencies> +		<dependency> +			<groupId>com.google.android</groupId> +			<artifactId>android</artifactId> +			<scope>provided</scope> +		</dependency> +		<dependency> +			<groupId>com.google.android</groupId> +			<artifactId>support-v4</artifactId> +		</dependency> + +		<dependency> +			<groupId>junit</groupId> +			<artifactId>junit</artifactId> +			<scope>test</scope> +		</dependency> +	</dependencies> + +	<build> +		<sourceDirectory>src</sourceDirectory> +		<testSourceDirectory>test</testSourceDirectory> + +		<plugins> +			<plugin> +				<groupId>com.jayway.maven.plugins.android.generation2</groupId> +				<artifactId>android-maven-plugin</artifactId> +				<extensions>true</extensions> +				<configuration> +					<nativeLibrariesDirectory>ignored</nativeLibrariesDirectory> +				</configuration> +			</plugin> + +			<plugin> +				<groupId>org.apache.maven.plugins</groupId> +				<artifactId>maven-javadoc-plugin</artifactId> +				<configuration> +					<skip>true</skip> +				</configuration> +			</plugin> + +			<plugin> +				<groupId>com.google.code.maven-replacer-plugin</groupId> +				<artifactId>maven-replacer-plugin</artifactId> +				<version>1.4.0</version> +				<executions> +					<execution> +						<phase>process-sources</phase> +						<goals> +							<goal>replace</goal> +						</goals> +					</execution> +				</executions> +				<configuration> +					<ignoreMissingFile>false</ignoreMissingFile> +					<file>target/generated-sources/r/com/actionbarsherlock/R.java</file> +					<outputFile>target/generated-sources/r/com/actionbarsherlock/R.java</outputFile> +					<regex>false</regex> +					<token>static final int</token> +					<value>static int</value> +				</configuration> +			</plugin> + +			<plugin> +				<groupId>org.apache.maven.plugins</groupId> +				<artifactId>maven-checkstyle-plugin</artifactId> +				<configuration> +					<configLocation>../checkstyle.xml</configLocation> +				</configuration> +				<executions> +					<execution> +						<phase>verify</phase> +						<goals> +							<goal>checkstyle</goal> +						</goals> +					</execution> +				</executions> +			</plugin> + +			<plugin> +				<groupId>org.codehaus.mojo</groupId> +				<artifactId>build-helper-maven-plugin</artifactId> +				<version>1.7</version> +				<executions> +					<execution> +						<phase>package</phase> +						<goals> +							<goal>attach-artifact</goal> +						</goals> +						<configuration> +							<artifacts> +								<artifact> +									<type>jar</type> +									<file>${project.build.directory}/${project.build.finalName}.jar</file> +								</artifact> +							</artifacts> +						</configuration> +					</execution> +				</executions> +			</plugin> +		</plugins> + +		<pluginManagement> +			<plugins> +				<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.--> +				<plugin> +					<groupId>org.eclipse.m2e</groupId> +					<artifactId>lifecycle-mapping</artifactId> +					<version>1.0.0</version> +					<configuration> +						<lifecycleMappingMetadata> +							<pluginExecutions> +								<pluginExecution> +									<pluginExecutionFilter> +										<groupId>com.google.code.maven-replacer-plugin</groupId> +										<artifactId>maven-replacer-plugin</artifactId> +										<versionRange>[1.4.0,)</versionRange> +										<goals> +											<goal>replace</goal> +										</goals> +									</pluginExecutionFilter> +									<action> +										<ignore /> +									</action> +								</pluginExecution> +							</pluginExecutions> +						</lifecycleMappingMetadata> +					</configuration> +				</plugin> +			</plugins> +		</pluginManagement> +	</build> +</project> diff --git a/libraries/ActionBarSherlock/proguard-project.txt b/libraries/ActionBarSherlock/proguard-project.txt new file mode 100644 index 000000000..f2fe1559a --- /dev/null +++ b/libraries/ActionBarSherlock/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +#   http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +#   public *; +#} diff --git a/libraries/ActionBarSherlock/project.properties b/libraries/ActionBarSherlock/project.properties new file mode 100644 index 000000000..5ca7d6247 --- /dev/null +++ b/libraries/ActionBarSherlock/project.properties @@ -0,0 +1,12 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +android.library=true +# Project target. +target=android-14 diff --git a/libraries/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_dark.xml b/libraries/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_dark.xml new file mode 100644 index 000000000..ea7459aaf --- /dev/null +++ b/libraries/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_dark.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_enabled="false" android:color="@color/abs__bright_foreground_disabled_holo_dark"/> +    <item android:color="@color/abs__bright_foreground_holo_dark"/> <!-- not selected --> +</selector> diff --git a/libraries/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_light.xml b/libraries/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_light.xml new file mode 100644 index 000000000..0edb33b4b --- /dev/null +++ b/libraries/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_light.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_enabled="false" android:color="@color/abs__bright_foreground_disabled_holo_light"/> +    <item android:color="@color/abs__bright_foreground_holo_light"/> <!-- not selected --> +</selector> + diff --git a/libraries/ActionBarSherlock/res/color/abs__primary_text_holo_dark.xml b/libraries/ActionBarSherlock/res/color/abs__primary_text_holo_dark.xml new file mode 100644 index 000000000..2bcfd0b63 --- /dev/null +++ b/libraries/ActionBarSherlock/res/color/abs__primary_text_holo_dark.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_enabled="false" android:color="@color/abs__bright_foreground_disabled_holo_dark"/> +    <item android:state_window_focused="false" android:color="@color/abs__bright_foreground_holo_dark"/> +    <item android:state_pressed="true" android:color="@color/abs__bright_foreground_holo_dark"/> +    <item android:state_selected="true" android:color="@color/abs__bright_foreground_holo_dark"/> +    <item android:state_activated="true" android:color="@color/abs__bright_foreground_holo_dark"/> +    <item android:color="@color/abs__bright_foreground_holo_dark"/> <!-- not selected --> +</selector> diff --git a/libraries/ActionBarSherlock/res/color/abs__primary_text_holo_light.xml b/libraries/ActionBarSherlock/res/color/abs__primary_text_holo_light.xml new file mode 100644 index 000000000..198384fed --- /dev/null +++ b/libraries/ActionBarSherlock/res/color/abs__primary_text_holo_light.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_enabled="false" android:color="@color/abs__bright_foreground_disabled_holo_light"/> +    <item android:state_window_focused="false" android:color="@color/abs__bright_foreground_holo_light"/> +    <item android:state_pressed="true" android:color="@color/abs__bright_foreground_holo_light"/> +    <item android:state_selected="true" android:color="@color/abs__bright_foreground_holo_light"/> +    <item android:state_activated="true" android:color="@color/abs__bright_foreground_holo_light"/> +    <item android:color="@color/abs__bright_foreground_holo_light"/> <!-- not selected --> +     +</selector> + diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..769463b36 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_inverse_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_inverse_holo.9.png Binary files differnew file mode 100644 index 000000000..88f11dcb9 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_inverse_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png Binary files differnew file mode 100644 index 000000000..73050476e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..712a551ec --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_light_holo.9.png Binary files differnew file mode 100644 index 000000000..bf3b9438b --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..81b87b86c --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_light.9.png Binary files differnew file mode 100644 index 000000000..8fc83e22e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..cbbaec588 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_light_holo.9.png Binary files differnew file mode 100644 index 000000000..af917e5b6 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_shadow_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_shadow_holo.9.png Binary files differnew file mode 100644 index 000000000..2d59f354e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_shadow_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_solid_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_solid_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..0520e5a2f --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_solid_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_solid_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_solid_light_holo.9.png Binary files differnew file mode 100644 index 000000000..e3e3f93b9 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_solid_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..1e3957222 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png Binary files differnew file mode 100644 index 000000000..a16db853e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..0eff695d8 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_light_holo.9.png Binary files differnew file mode 100644 index 000000000..219b170fa --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_default_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_default_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..b0dc31fb3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_default_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_default_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_default_holo_light.9.png Binary files differnew file mode 100644 index 000000000..4bc2683b1 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_default_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_focused_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_focused_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..4af38fb70 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_focused_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_focused_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_focused_holo_light.9.png Binary files differnew file mode 100644 index 000000000..d32f74cf4 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_focused_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..66adffed6 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_light.9.png Binary files differnew file mode 100644 index 000000000..caeff9c33 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..1d836f65a --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_light.9.png Binary files differnew file mode 100644 index 000000000..5818666d4 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..564fb34b4 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_light.9.png Binary files differnew file mode 100644 index 000000000..ae21b760f --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..79e56f522 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_light.9.png Binary files differnew file mode 100644 index 000000000..e029f210b --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png Binary files differnew file mode 100644 index 000000000..897a1c11a --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_light.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_light.png Binary files differnew file mode 100644 index 000000000..0c89f7140 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_dark.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_dark.png Binary files differnew file mode 100644 index 000000000..d8662e3f0 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_dark.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_light.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_light.png Binary files differnew file mode 100644 index 000000000..ed03f620f --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_disabled.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_disabled.png Binary files differnew file mode 100644 index 000000000..d97c342d5 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_disabled.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_normal.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_normal.png Binary files differnew file mode 100644 index 000000000..33ad8d4b8 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_normal.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_search_api_disabled_holo_light.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_search_api_disabled_holo_light.png Binary files differnew file mode 100644 index 000000000..3edbd7408 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_search_api_disabled_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_search_api_holo_light.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..90db01b5b --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_search_api_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_go.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_go.png Binary files differnew file mode 100644 index 000000000..97b825e83 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_go.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_go_search_api_holo_light.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_go_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..7e1ba2adc --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_go_search_api_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png Binary files differnew file mode 100644 index 000000000..2abc45809 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_light.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_light.png Binary files differnew file mode 100644 index 000000000..bb6aef1d0 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_dark.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_dark.png Binary files differnew file mode 100644 index 000000000..6f747c8f0 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_dark.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_light.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_light.png Binary files differnew file mode 100644 index 000000000..682b2fdec --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_search.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_search.png Binary files differnew file mode 100644 index 000000000..4be72f108 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_search.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_search_api_holo_light.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..72e207bc5 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_search_api_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_voice_search.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_voice_search.png Binary files differnew file mode 100644 index 000000000..66d14aec0 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_voice_search.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_voice_search_api_holo_light.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_voice_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..3481c9828 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__ic_voice_search_api_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_activated_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_activated_holo.9.png Binary files differnew file mode 100644 index 000000000..4ea7afa00 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_activated_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_divider_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_divider_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..986ab0b97 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_divider_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_divider_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_divider_holo_light.9.png Binary files differnew file mode 100644 index 000000000..0279e17a1 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_divider_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_focused_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_focused_holo.9.png Binary files differnew file mode 100644 index 000000000..516f5c739 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_focused_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_longpressed_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_longpressed_holo.9.png Binary files differnew file mode 100644 index 000000000..4ea7afa00 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_longpressed_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_pressed_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..5654cd694 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_pressed_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_pressed_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_pressed_holo_light.9.png Binary files differnew file mode 100644 index 000000000..5654cd694 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_pressed_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_selector_disabled_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_selector_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..f6fd30dcd --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_selector_disabled_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_selector_disabled_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_selector_disabled_holo_light.9.png Binary files differnew file mode 100644 index 000000000..ca8e9a277 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__list_selector_disabled_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..4d3d20857 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_light.9.png Binary files differnew file mode 100644 index 000000000..924a99d17 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..310c368e7 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_light.9.png Binary files differnew file mode 100644 index 000000000..70cb7fc7e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..1c269205e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_light.9.png Binary files differnew file mode 100644 index 000000000..1c269205e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..40d0d1645 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_light.9.png Binary files differnew file mode 100644 index 000000000..40d0d1645 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_inner_holo.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_inner_holo.png Binary files differnew file mode 100644 index 000000000..c8358e9ce --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_inner_holo.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_outer_holo.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_outer_holo.png Binary files differnew file mode 100644 index 000000000..f62f74bb3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_outer_holo.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..eb28ff9a5 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_light.9.png Binary files differnew file mode 100644 index 000000000..d281adb55 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..b29858609 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_light.9.png Binary files differnew file mode 100644 index 000000000..4215396dd --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..a280eabf5 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_light.9.png Binary files differnew file mode 100644 index 000000000..f8d619b4d --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..955a2f340 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_light.9.png Binary files differnew file mode 100644 index 000000000..6c22e223a --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png Binary files differnew file mode 100644 index 000000000..673e3bf10 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_holo.9.png Binary files differnew file mode 100644 index 000000000..d57df98b5 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_pressed_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_pressed_holo.9.png Binary files differnew file mode 100644 index 000000000..6278eef47 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_pressed_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__tab_unselected_pressed_holo.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__tab_unselected_pressed_holo.9.png Binary files differnew file mode 100644 index 000000000..aadc6f87b --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__tab_unselected_pressed_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_default_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_default_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..70c0e7396 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_default_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_default_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_default_holo_light.9.png Binary files differnew file mode 100644 index 000000000..36e71d85d --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_default_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_default_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_default_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..4be4af5fa --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_default_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_default_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_default_holo_light.9.png Binary files differnew file mode 100644 index 000000000..e72193f59 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_default_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_selected_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_selected_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..8f20b9d26 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_selected_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_selected_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_selected_holo_light.9.png Binary files differnew file mode 100644 index 000000000..04f657e1d --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_selected_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_selected_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_selected_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..99309ef6d --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_selected_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_selected_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_selected_holo_light.9.png Binary files differnew file mode 100644 index 000000000..9bde7fbdc --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_selected_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..b2293670b --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_inverse_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_inverse_holo.9.png Binary files differnew file mode 100644 index 000000000..c65f443e3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_inverse_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png Binary files differnew file mode 100644 index 000000000..0706c8af6 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..d814d02d3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_light_holo.9.png Binary files differnew file mode 100644 index 000000000..b139c8e49 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..738cb38d0 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_light.9.png Binary files differnew file mode 100644 index 000000000..2ed75a767 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..743d00b6c --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_light_holo.9.png Binary files differnew file mode 100644 index 000000000..17c1fb921 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_shadow_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_shadow_holo.9.png Binary files differnew file mode 100644 index 000000000..ddfc8e3d5 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_shadow_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..007a4b239 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_light_holo.9.png Binary files differnew file mode 100644 index 000000000..ad6e1a4d9 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..0ad6c888b --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png Binary files differnew file mode 100644 index 000000000..19b50abcb --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_transparent_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_transparent_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..ad980b13f --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_transparent_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_transparent_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_transparent_light_holo.9.png Binary files differnew file mode 100644 index 000000000..60e6c5278 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ab_transparent_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..5461b9c00 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_light.9.png Binary files differnew file mode 100644 index 000000000..5dc6f804a --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..a70b53c59 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_light.9.png Binary files differnew file mode 100644 index 000000000..c7a9896b0 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..85d7aadd4 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_light.9.png Binary files differnew file mode 100644 index 000000000..f7b01e012 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..d8f1c8bd5 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_light.9.png Binary files differnew file mode 100644 index 000000000..31e49894a --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_top_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_top_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..7c2cbe535 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_top_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_top_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_top_holo_light.9.png Binary files differnew file mode 100644 index 000000000..30cbdc174 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_top_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..fb3660eab --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_light.9.png Binary files differnew file mode 100644 index 000000000..f18050ea5 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png Binary files differnew file mode 100644 index 000000000..df2d3d158 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_light.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_light.png Binary files differnew file mode 100644 index 000000000..b2aa9c265 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_dark.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_dark.png Binary files differnew file mode 100644 index 000000000..a17b6a789 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_dark.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_light.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_light.png Binary files differnew file mode 100644 index 000000000..b28b3b54f --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_disabled.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_disabled.png Binary files differnew file mode 100644 index 000000000..79228baed --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_disabled.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_normal.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_normal.png Binary files differnew file mode 100644 index 000000000..86944a879 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_normal.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_search_api_disabled_holo_light.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_search_api_disabled_holo_light.png Binary files differnew file mode 100644 index 000000000..c0bdf0641 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_search_api_disabled_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_search_api_holo_light.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..15b86cbb2 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_search_api_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_go.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_go.png Binary files differnew file mode 100644 index 000000000..bf19833f2 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_go.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_go_search_api_holo_light.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_go_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..8518498eb --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_go_search_api_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png Binary files differnew file mode 100644 index 000000000..ba704b67e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_light.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_light.png Binary files differnew file mode 100644 index 000000000..01d681697 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_dark.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_dark.png Binary files differnew file mode 100644 index 000000000..6bf21e307 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_dark.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_light.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_light.png Binary files differnew file mode 100644 index 000000000..70fe31aa2 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_search.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_search.png Binary files differnew file mode 100644 index 000000000..4be72f108 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_search.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_search_api_holo_light.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..f2e26f883 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_search_api_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_voice_search.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_voice_search.png Binary files differnew file mode 100644 index 000000000..73c6be654 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_voice_search.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_voice_search_api_holo_light.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_voice_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..71d838e73 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__ic_voice_search_api_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_activated_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_activated_holo.9.png Binary files differnew file mode 100644 index 000000000..3bf8e0362 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_activated_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..986ab0b97 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_light.9.png Binary files differnew file mode 100644 index 000000000..0279e17a1 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_focused_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_focused_holo.9.png Binary files differnew file mode 100644 index 000000000..7c0599e3a --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_focused_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_longpressed_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_longpressed_holo.9.png Binary files differnew file mode 100644 index 000000000..3bf8e0362 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_longpressed_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..6e77525d2 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_light.9.png Binary files differnew file mode 100644 index 000000000..6e77525d2 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_selector_disabled_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_selector_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..92da2f0dd --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_selector_disabled_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_selector_disabled_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_selector_disabled_holo_light.9.png Binary files differnew file mode 100644 index 000000000..42cb6463e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__list_selector_disabled_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..460ec46eb --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_light.9.png Binary files differnew file mode 100644 index 000000000..e84adf2d4 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_bg_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_bg_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..3d946e545 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_bg_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_bg_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_bg_holo_light.9.png Binary files differnew file mode 100644 index 000000000..4bb22f0e1 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_bg_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..ab8ec6984 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_light.9.png Binary files differnew file mode 100644 index 000000000..ab8ec6984 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..7274274b1 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_light.9.png Binary files differnew file mode 100644 index 000000000..7274274b1 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_inner_holo.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_inner_holo.png Binary files differnew file mode 100644 index 000000000..9458668f0 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_inner_holo.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_outer_holo.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_outer_holo.png Binary files differnew file mode 100644 index 000000000..4ce73edce --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_outer_holo.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..29aff4d43 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_light.9.png Binary files differnew file mode 100644 index 000000000..4055f7053 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..ea4ee042e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_light.9.png Binary files differnew file mode 100644 index 000000000..f74c02b9e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..09a2992cc --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_light.9.png Binary files differnew file mode 100644 index 000000000..6536ee633 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..202b5b72e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_light.9.png Binary files differnew file mode 100644 index 000000000..6de0ba884 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png Binary files differnew file mode 100644 index 000000000..c9972e74b --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_holo.9.png Binary files differnew file mode 100644 index 000000000..587337caf --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_pressed_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_pressed_holo.9.png Binary files differnew file mode 100644 index 000000000..155c4fc75 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_pressed_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__tab_unselected_pressed_holo.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__tab_unselected_pressed_holo.9.png Binary files differnew file mode 100644 index 000000000..b1223fe3c --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__tab_unselected_pressed_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_default_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_default_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..081657ee7 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_default_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_default_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_default_holo_light.9.png Binary files differnew file mode 100644 index 000000000..3f312b465 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_default_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_default_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_default_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..b086fae87 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_default_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_default_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_default_holo_light.9.png Binary files differnew file mode 100644 index 000000000..73c336a77 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_default_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_selected_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_selected_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..726e0ff42 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_selected_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_selected_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_selected_holo_light.9.png Binary files differnew file mode 100644 index 000000000..726e0ff42 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_selected_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_selected_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_selected_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..1767c169e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_selected_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_selected_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_selected_holo_light.9.png Binary files differnew file mode 100644 index 000000000..1767c169e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_selected_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-v11/abs__progress_medium_holo.xml b/libraries/ActionBarSherlock/res/drawable-v11/abs__progress_medium_holo.xml new file mode 100644 index 000000000..6bcbdb83f --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-v11/abs__progress_medium_holo.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2010, 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. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> +    <item> +        <rotate +             android:drawable="@drawable/abs__spinner_48_outer_holo" +             android:pivotX="50%" +             android:pivotY="50%" +             android:fromDegrees="0" +             android:toDegrees="1080" /> +    </item> +    <item> +        <rotate +             android:drawable="@drawable/abs__spinner_48_inner_holo" +             android:pivotX="50%" +             android:pivotY="50%" +             android:fromDegrees="720" +             android:toDegrees="0" /> +    </item> +</layer-list> diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..575334699 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_inverse_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_inverse_holo.9.png Binary files differnew file mode 100644 index 000000000..7e6c047d6 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_inverse_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png Binary files differnew file mode 100644 index 000000000..8155fe840 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..6cee9a128 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_light_holo.9.png Binary files differnew file mode 100644 index 000000000..fa4d76af9 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..9a70a5d1e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_light.9.png Binary files differnew file mode 100644 index 000000000..14fbee101 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..6622cbad3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_light_holo.9.png Binary files differnew file mode 100644 index 000000000..c42729783 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_shadow_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_shadow_holo.9.png Binary files differnew file mode 100644 index 000000000..d0df29d8b --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_shadow_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..a0d9c1b95 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_light_holo.9.png Binary files differnew file mode 100644 index 000000000..d36f99fec --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..5ad475dc3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_light_holo.9.png Binary files differnew file mode 100644 index 000000000..6ade5eeb3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_dark_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_dark_holo.9.png Binary files differnew file mode 100644 index 000000000..719b9234d --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_dark_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_light_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_light_holo.9.png Binary files differnew file mode 100644 index 000000000..6da264db2 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_light_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..7ef2db75e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_light.9.png Binary files differnew file mode 100644 index 000000000..2283b4c01 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..6d2039e28 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_light.9.png Binary files differnew file mode 100644 index 000000000..3c909b513 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..131d1030c --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_light.9.png Binary files differnew file mode 100644 index 000000000..3e7dcdfdb --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..0bd09806f --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_light.9.png Binary files differnew file mode 100644 index 000000000..43ed26d47 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..6b3157985 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_light.9.png Binary files differnew file mode 100644 index 000000000..df0121bb3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..f4970ad1c --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_light.9.png Binary files differnew file mode 100644 index 000000000..172fc3b5e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_dark.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_dark.png Binary files differnew file mode 100644 index 000000000..8ded62fb7 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_dark.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_light.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_light.png Binary files differnew file mode 100644 index 000000000..517e9f72d --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_dark.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_dark.png Binary files differnew file mode 100644 index 000000000..2e06dd01b --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_dark.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_light.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_light.png Binary files differnew file mode 100644 index 000000000..bb19810bc --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_disabled.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_disabled.png Binary files differnew file mode 100644 index 000000000..e35c5f05e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_disabled.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_search_api_disabled_holo_light.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_search_api_disabled_holo_light.png Binary files differnew file mode 100644 index 000000000..7fd7aeb2a --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_search_api_disabled_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_search_api_holo_light.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..53cfbd311 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_search_api_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_go.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_go.png Binary files differnew file mode 100644 index 000000000..1e2dcfa02 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_go.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_go_search_api_holo_light.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_go_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..f12eafcdc --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_go_search_api_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png Binary files differnew file mode 100644 index 000000000..a92fb1d4a --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_light.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_light.png Binary files differnew file mode 100644 index 000000000..930ca8d95 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_dark.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_dark.png Binary files differnew file mode 100644 index 000000000..45a0f1da0 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_dark.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_light.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_light.png Binary files differnew file mode 100644 index 000000000..528e554ab --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_search.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_search.png Binary files differnew file mode 100644 index 000000000..998f91be9 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_search.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_search_api_holo_light.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..a4cdf1c79 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_search_api_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_voice_search.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_voice_search.png Binary files differnew file mode 100644 index 000000000..c625a3602 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_voice_search.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_voice_search_api_holo_light.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_voice_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..c332ba08c --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__ic_voice_search_api_holo_light.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_activated_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_activated_holo.9.png Binary files differnew file mode 100644 index 000000000..eda10e612 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_activated_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..e62f011d4 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_light.9.png Binary files differnew file mode 100644 index 000000000..65061c0f4 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_focused_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_focused_holo.9.png Binary files differnew file mode 100644 index 000000000..690cb1eb6 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_focused_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_longpressed_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_longpressed_holo.9.png Binary files differnew file mode 100644 index 000000000..eda10e612 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_longpressed_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..e4b33935a --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_light.9.png Binary files differnew file mode 100644 index 000000000..e4b33935a --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..88726b691 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_light.9.png Binary files differnew file mode 100644 index 000000000..c6a7d4d87 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..e2aff72f4 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_light.9.png Binary files differnew file mode 100644 index 000000000..93066c840 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..345f5d306 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_light.9.png Binary files differnew file mode 100644 index 000000000..c843ef3af --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_primary_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_primary_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..c6c3f1ec2 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_primary_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_primary_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_primary_holo_light.9.png Binary files differnew file mode 100644 index 000000000..c6c3f1ec2 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_primary_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_secondary_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_secondary_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..205b66e2c --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_secondary_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_secondary_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_secondary_holo_light.9.png Binary files differnew file mode 100644 index 000000000..205b66e2c --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__progress_secondary_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_inner_holo.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_inner_holo.png Binary files differnew file mode 100644 index 000000000..19517c4b0 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_inner_holo.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_outer_holo.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_outer_holo.png Binary files differnew file mode 100644 index 000000000..14143c51c --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_outer_holo.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..d8929fcd1 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_light.9.png Binary files differnew file mode 100644 index 000000000..9174c4e4b --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..3015d3070 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_light.9.png Binary files differnew file mode 100644 index 000000000..126637d11 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..d45c7a864 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_light.9.png Binary files differnew file mode 100644 index 000000000..29036b907 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..2cb34d7f6 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_light.9.png Binary files differnew file mode 100644 index 000000000..82f752fdc --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_focused_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_focused_holo.9.png Binary files differnew file mode 100644 index 000000000..03cfb0945 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_focused_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_holo.9.png Binary files differnew file mode 100644 index 000000000..e4229f26b --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_pressed_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_pressed_holo.9.png Binary files differnew file mode 100644 index 000000000..e862cb121 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_pressed_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__tab_unselected_pressed_holo.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__tab_unselected_pressed_holo.9.png Binary files differnew file mode 100644 index 000000000..f1eb67323 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__tab_unselected_pressed_holo.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_default_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_default_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..8fdbbf3ad --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_default_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_default_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_default_holo_light.9.png Binary files differnew file mode 100644 index 000000000..4e9ae43c2 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_default_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_default_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_default_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..98f4871bb --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_default_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_default_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_default_holo_light.9.png Binary files differnew file mode 100644 index 000000000..733373ed3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_default_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..0c6bb036d --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_light.9.png Binary files differnew file mode 100644 index 000000000..0c6bb036d --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_selected_holo_dark.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_selected_holo_dark.9.png Binary files differnew file mode 100644 index 000000000..e5bfd8ad3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_selected_holo_dark.9.png diff --git a/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_selected_holo_light.9.png b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_selected_holo_light.9.png Binary files differnew file mode 100644 index 000000000..1743da6b4 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_selected_holo_light.9.png diff --git a/libraries/ActionBarSherlock/res/drawable/abs__activated_background_holo_dark.xml b/libraries/ActionBarSherlock/res/drawable/abs__activated_background_holo_dark.xml new file mode 100644 index 000000000..85c2c0212 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__activated_background_holo_dark.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_activated="true" android:drawable="@drawable/abs__list_activated_holo" /> +    <item android:drawable="@android:color/transparent" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__activated_background_holo_light.xml b/libraries/ActionBarSherlock/res/drawable/abs__activated_background_holo_light.xml new file mode 100644 index 000000000..85c2c0212 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__activated_background_holo_light.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_activated="true" android:drawable="@drawable/abs__list_activated_holo" /> +    <item android:drawable="@android:color/transparent" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_dark.xml b/libraries/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_dark.xml new file mode 100644 index 000000000..cab896283 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_dark.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_pressed="true" +        android:drawable="@drawable/abs__btn_cab_done_pressed_holo_dark" /> +    <item android:state_focused="true" android:state_enabled="true" +        android:drawable="@drawable/abs__btn_cab_done_focused_holo_dark" /> +    <item android:state_enabled="true" +        android:drawable="@drawable/abs__btn_cab_done_default_holo_dark" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_light.xml b/libraries/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_light.xml new file mode 100644 index 000000000..42ba8a0df --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_light.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_pressed="true" +        android:drawable="@drawable/abs__btn_cab_done_pressed_holo_light" /> +    <item android:state_focused="true" android:state_enabled="true" +        android:drawable="@drawable/abs__btn_cab_done_focused_holo_light" /> +    <item android:state_enabled="true" +        android:drawable="@drawable/abs__btn_cab_done_default_holo_light" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__ic_clear.xml b/libraries/ActionBarSherlock/res/drawable/abs__ic_clear.xml new file mode 100644 index 000000000..a16f4b22e --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__ic_clear.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_enabled="false" +        android:drawable="@drawable/abs__ic_clear_disabled" /> +    <item +         android:drawable="@drawable/abs__ic_clear_normal" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__ic_clear_holo_light.xml b/libraries/ActionBarSherlock/res/drawable/abs__ic_clear_holo_light.xml new file mode 100644 index 000000000..256de80fb --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__ic_clear_holo_light.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_enabled="false" +        android:drawable="@drawable/abs__ic_clear_search_api_disabled_holo_light" /> +    <item +         android:drawable="@drawable/abs__ic_clear_search_api_holo_light" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_dark.xml b/libraries/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_dark.xml new file mode 100644 index 000000000..2588a492d --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_dark.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:drawable="@drawable/abs__ic_menu_moreoverflow_normal_holo_dark" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_light.xml b/libraries/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_light.xml new file mode 100644 index 000000000..e2078c967 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_light.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:drawable="@drawable/abs__ic_menu_moreoverflow_normal_holo_light" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__item_background_holo_dark.xml b/libraries/ActionBarSherlock/res/drawable/abs__item_background_holo_dark.xml new file mode 100644 index 000000000..d99b7a426 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__item_background_holo_dark.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + +    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. --> +    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/abs__list_selector_disabled_holo_dark" /> +    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/abs__list_selector_disabled_holo_dark" /> +    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/abs__list_selector_background_transition_holo_dark" /> +    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/abs__list_selector_background_transition_holo_dark" /> +    <item android:state_focused="true"                                                             android:drawable="@drawable/abs__list_focused_holo" /> +    <item                                                                                          android:drawable="@android:color/transparent" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__item_background_holo_light.xml b/libraries/ActionBarSherlock/res/drawable/abs__item_background_holo_light.xml new file mode 100644 index 000000000..da5fb2e86 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__item_background_holo_light.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + +    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. --> +    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/abs__list_selector_disabled_holo_light" /> +    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/abs__list_selector_disabled_holo_light" /> +    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/abs__list_selector_background_transition_holo_light" /> +    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/abs__list_selector_background_transition_holo_light" /> +    <item android:state_focused="true"                                                             android:drawable="@drawable/abs__list_focused_holo" /> +    <item                                                                                          android:drawable="@android:color/transparent" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_dark.xml b/libraries/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_dark.xml new file mode 100644 index 000000000..b2ce4f0f7 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_dark.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<transition xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:drawable="@drawable/abs__list_pressed_holo_dark"  /> +    <item android:drawable="@drawable/abs__list_longpressed_holo"  /> +</transition> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_light.xml b/libraries/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_light.xml new file mode 100644 index 000000000..d7e31b1d1 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_light.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<transition xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:drawable="@drawable/abs__list_pressed_holo_light"  /> +    <item android:drawable="@drawable/abs__list_longpressed_holo"  /> +</transition> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__list_selector_holo_dark.xml b/libraries/ActionBarSherlock/res/drawable/abs__list_selector_holo_dark.xml new file mode 100644 index 000000000..08b8b12f3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__list_selector_holo_dark.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + +    <item android:state_window_focused="false" android:drawable="@android:color/transparent" /> + +    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. --> +    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/abs__list_selector_disabled_holo_dark" /> +    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/abs__list_selector_disabled_holo_dark" /> +    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/abs__list_selector_background_transition_holo_dark" /> +    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/abs__list_selector_background_transition_holo_dark" /> +    <item android:state_focused="true"                                                             android:drawable="@drawable/abs__list_focused_holo" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__list_selector_holo_light.xml b/libraries/ActionBarSherlock/res/drawable/abs__list_selector_holo_light.xml new file mode 100644 index 000000000..ada490bf9 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__list_selector_holo_light.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + +    <item android:state_window_focused="false" android:drawable="@android:color/transparent" /> + +    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. --> +    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/abs__list_selector_disabled_holo_light" /> +    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/abs__list_selector_disabled_holo_light" /> +    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/abs__list_selector_background_transition_holo_light" /> +    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/abs__list_selector_background_transition_holo_light" /> +    <item android:state_focused="true"                                                             android:drawable="@drawable/abs__list_focused_holo" /> + +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_dark.xml b/libraries/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_dark.xml new file mode 100644 index 000000000..bd19140ab --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_dark.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + +    <item android:id="@android:id/background" +          android:drawable="@drawable/abs__progress_bg_holo_dark" /> + +    <item android:id="@android:id/secondaryProgress"> +        <scale android:scaleWidth="100%" +               android:drawable="@drawable/abs__progress_secondary_holo_dark" /> +    </item> + +    <item android:id="@android:id/progress"> +        <scale android:scaleWidth="100%" +               android:drawable="@drawable/abs__progress_primary_holo_dark" /> +    </item> + +</layer-list> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_light.xml b/libraries/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_light.xml new file mode 100644 index 000000000..321f07c8b --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_light.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + +    <item android:id="@android:id/background" +          android:drawable="@drawable/abs__progress_bg_holo_light" /> + +    <item android:id="@android:id/secondaryProgress"> +        <scale android:scaleWidth="100%" +               android:drawable="@drawable/abs__progress_secondary_holo_light" /> +    </item> + +    <item android:id="@android:id/progress"> +        <scale android:scaleWidth="100%" +               android:drawable="@drawable/abs__progress_primary_holo_light" /> +    </item> + +</layer-list> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__progress_medium_holo.xml b/libraries/ActionBarSherlock/res/drawable/abs__progress_medium_holo.xml new file mode 100644 index 000000000..6d4814f86 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__progress_medium_holo.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2010, 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. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> +    <item> +        <rotate +             android:drawable="@drawable/abs__spinner_48_outer_holo" +             android:pivotX="50%" +             android:pivotY="50%" +             android:fromDegrees="0" +             android:toDegrees="1080" /> +    </item> +    <item> +        <rotate +             android:drawable="@drawable/abs__spinner_48_inner_holo" +             android:pivotX="50%" +             android:pivotY="50%" +             android:fromDegrees="0" +             android:toDegrees="720" /> +    </item> +</layer-list> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__search_dropdown_dark.xml b/libraries/ActionBarSherlock/res/drawable/abs__search_dropdown_dark.xml new file mode 100644 index 000000000..26284187a --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__search_dropdown_dark.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> +    <solid android:color="#F0A0A0A0"/> +    <stroke android:width="2dp" color="#A00080FF"/> +    <padding android:left="5dp" android:top="0dp" +        android:right="5dp" android:bottom="1dp" /> +</shape> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__search_dropdown_light.xml b/libraries/ActionBarSherlock/res/drawable/abs__search_dropdown_light.xml new file mode 100644 index 000000000..0d00c5878 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__search_dropdown_light.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> +    <solid android:color="#F0FFFFFF"/> +    <stroke android:width="1dp" color="#A00080FF"/> +    <padding android:left="5dp" android:top="0dp" +        android:right="5dp" android:bottom="1dp" /> +</shape> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_dark.xml b/libraries/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_dark.xml new file mode 100644 index 000000000..4af5e22a9 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_dark.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_enabled="false" +          android:drawable="@drawable/abs__spinner_ab_disabled_holo_dark" /> +    <item android:state_pressed="true" +          android:drawable="@drawable/abs__spinner_ab_pressed_holo_dark" /> +    <item android:state_pressed="false" android:state_focused="true" +          android:drawable="@drawable/abs__spinner_ab_focused_holo_dark" /> +    <item android:drawable="@drawable/abs__spinner_ab_default_holo_dark" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_light.xml b/libraries/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_light.xml new file mode 100644 index 000000000..b78508478 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_light.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_enabled="false" +          android:drawable="@drawable/abs__spinner_ab_disabled_holo_light" /> +    <item android:state_pressed="true" +          android:drawable="@drawable/abs__spinner_ab_pressed_holo_light" /> +    <item android:state_pressed="false" android:state_focused="true" +          android:drawable="@drawable/abs__spinner_ab_focused_holo_light" /> +    <item android:drawable="@drawable/abs__spinner_ab_default_holo_light" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__tab_indicator_ab_holo.xml b/libraries/ActionBarSherlock/res/drawable/abs__tab_indicator_ab_holo.xml new file mode 100644 index 000000000..d34e20811 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__tab_indicator_ab_holo.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <!-- Non focused states --> +    <item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@android:color/transparent" /> +    <item android:state_focused="false" android:state_selected="true"  android:state_pressed="false" android:drawable="@drawable/abs__tab_selected_holo" /> + +    <!-- Focused states --> +    <item android:state_focused="true" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/abs__list_focused_holo" /> +    <item android:state_focused="true" android:state_selected="true"  android:state_pressed="false" android:drawable="@drawable/abs__tab_selected_focused_holo" /> + +    <!-- Pressed --> +    <!--    Non focused states --> +    <item android:state_focused="false" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/abs__list_pressed_holo_dark" /> +    <item android:state_focused="false" android:state_selected="true"  android:state_pressed="true" android:drawable="@drawable/abs__tab_selected_pressed_holo" /> + +    <!--    Focused states --> +    <item android:state_focused="true" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/abs__tab_unselected_pressed_holo" /> +    <item android:state_focused="true" android:state_selected="true"  android:state_pressed="true" android:drawable="@drawable/abs__tab_selected_pressed_holo" /> +</selector> diff --git a/libraries/ActionBarSherlock/res/drawable/abs__textfield_searchview_holo_dark.xml b/libraries/ActionBarSherlock/res/drawable/abs__textfield_searchview_holo_dark.xml new file mode 100644 index 000000000..b6d58c040 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__textfield_searchview_holo_dark.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_focused="true" +        android:drawable="@drawable/abs__textfield_search_selected_holo_dark" /> +    <item android:drawable="@drawable/abs__textfield_search_default_holo_dark" /> +</selector> + diff --git a/libraries/ActionBarSherlock/res/drawable/abs__textfield_searchview_holo_light.xml b/libraries/ActionBarSherlock/res/drawable/abs__textfield_searchview_holo_light.xml new file mode 100644 index 000000000..3d6acf808 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__textfield_searchview_holo_light.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_focused="true" +        android:drawable="@drawable/abs__textfield_search_selected_holo_light" /> +    <item android:drawable="@drawable/abs__textfield_search_default_holo_light" /> +</selector> + diff --git a/libraries/ActionBarSherlock/res/drawable/abs__textfield_searchview_right_holo_dark.xml b/libraries/ActionBarSherlock/res/drawable/abs__textfield_searchview_right_holo_dark.xml new file mode 100644 index 000000000..05ff4eda5 --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__textfield_searchview_right_holo_dark.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_focused="true" +        android:drawable="@drawable/abs__textfield_search_right_selected_holo_dark" /> +    <item android:drawable="@drawable/abs__textfield_search_right_default_holo_dark" /> +</selector> + diff --git a/libraries/ActionBarSherlock/res/drawable/abs__textfield_searchview_right_holo_light.xml b/libraries/ActionBarSherlock/res/drawable/abs__textfield_searchview_right_holo_light.xml new file mode 100644 index 000000000..f6d61e57a --- /dev/null +++ b/libraries/ActionBarSherlock/res/drawable/abs__textfield_searchview_right_holo_light.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +    <item android:state_focused="true" +        android:drawable="@drawable/abs__textfield_search_right_selected_holo_light" /> +    <item android:drawable="@drawable/abs__textfield_search_right_default_holo_light" /> +</selector> + diff --git a/libraries/ActionBarSherlock/res/layout-large/abs__action_mode_close_item.xml b/libraries/ActionBarSherlock/res/layout-large/abs__action_mode_close_item.xml new file mode 100644 index 000000000..8811dad8d --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout-large/abs__action_mode_close_item.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<com.actionbarsherlock.internal.nineoldandroids.widget.NineLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +        android:id="@+id/abs__action_mode_close_button" +        android:focusable="true" +        android:clickable="true" +        android:paddingLeft="8dip" +        style="?attr/actionModeCloseButtonStyle" +        android:layout_width="wrap_content" +        android:layout_height="match_parent" +        android:layout_marginRight="16dip"> +    <ImageView android:layout_width="48dip" +               android:layout_height="wrap_content" +               android:layout_gravity="center" +               android:scaleType="center" +               android:src="?attr/actionModeCloseDrawable" /> +    <TextView android:layout_width="wrap_content" +              android:layout_height="wrap_content" +              android:layout_gravity="center" +              android:layout_marginLeft="4dip" +              android:layout_marginRight="16dip" +              android:textAppearance="?android:attr/textAppearanceSmall" +              android:textSize="12sp" +              android:textAllCaps="true" +              android:text="@string/abs__action_mode_done" /> +</com.actionbarsherlock.internal.nineoldandroids.widget.NineLinearLayout> diff --git a/libraries/ActionBarSherlock/res/layout-v14/sherlock_spinner_dropdown_item.xml b/libraries/ActionBarSherlock/res/layout-v14/sherlock_spinner_dropdown_item.xml new file mode 100644 index 000000000..6c183c059 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout-v14/sherlock_spinner_dropdown_item.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/layout/simple_spinner_item.xml +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License");  +** you may not use this file except in compliance with the License.  +** You may obtain a copy of the License at  +** +**     http://www.apache.org/licenses/LICENSE-2.0  +** +** Unless required by applicable law or agreed to in writing, software  +** distributed under the License is distributed on an "AS IS" BASIS,  +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  +** See the License for the specific language governing permissions and  +** limitations under the License. +*/ +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android"  +    android:id="@android:id/text1" +    style="?android:attr/spinnerDropDownItemStyle" +    android:singleLine="true" +    android:layout_width="match_parent" +    android:layout_height="?attr/dropdownListPreferredItemHeight" +    android:ellipsize="marquee" /> diff --git a/libraries/ActionBarSherlock/res/layout-v14/sherlock_spinner_item.xml b/libraries/ActionBarSherlock/res/layout-v14/sherlock_spinner_item.xml new file mode 100644 index 000000000..61dc02527 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout-v14/sherlock_spinner_item.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/layout/simple_spinner_item.xml +** +** Copyright 2006, 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. +*/ +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android"  +    android:id="@android:id/text1" +    style="?android:attr/spinnerItemStyle" +    android:singleLine="true" +    android:layout_width="match_parent" +    android:layout_height="wrap_content" +    android:ellipsize="marquee" /> diff --git a/libraries/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar.xml b/libraries/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar.xml new file mode 100644 index 000000000..040df44ab --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<!-- +This is an optimized layout for a screen with the Action Bar enabled. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:orientation="vertical" +    android:fitsSystemWindows="true" +    android:layout_width="fill_parent" +    android:layout_height="fill_parent"> +    <com.actionbarsherlock.internal.widget.ActionBarContainer +        android:id="@+id/abs__action_bar_container" +        android:layout_width="fill_parent" +        android:layout_height="wrap_content" +        style="?attr/actionBarStyle"> +        <com.actionbarsherlock.internal.widget.ActionBarView +            android:id="@+id/abs__action_bar" +            android:layout_width="fill_parent" +            android:layout_height="wrap_content" +            style="?attr/actionBarStyle" /> +        <com.actionbarsherlock.internal.widget.ActionBarContextView +            android:id="@+id/abs__action_context_bar" +            android:layout_width="fill_parent" +            android:layout_height="wrap_content" +            android:visibility="gone" +            style="?attr/actionModeStyle" /> +    </com.actionbarsherlock.internal.widget.ActionBarContainer> +    <com.actionbarsherlock.internal.nineoldandroids.widget.NineFrameLayout +        android:id="@+id/abs__content" +        android:layout_width="fill_parent" +        android:layout_height="0dip" +        android:layout_weight="1" +        android:foregroundGravity="fill_horizontal|top" +        android:foreground="?attr/windowContentOverlay" /> +</LinearLayout> diff --git a/libraries/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar_overlay.xml b/libraries/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar_overlay.xml new file mode 100644 index 000000000..c64ef141b --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar_overlay.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<!-- +This is an optimized layout for a screen with +the Action Bar enabled overlaying application content. +--> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:fitsSystemWindows="true"> +    <com.actionbarsherlock.internal.nineoldandroids.widget.NineFrameLayout android:id="@+id/abs__content" +        android:layout_width="fill_parent" +        android:layout_height="fill_parent" /> +    <com.actionbarsherlock.internal.widget.ActionBarContainer android:id="@+id/abs__action_bar_container" +        android:layout_width="fill_parent" +        android:layout_height="wrap_content" +        style="?attr/actionBarStyle" +        android:gravity="top"> +        <com.actionbarsherlock.internal.widget.ActionBarView +            android:id="@+id/abs__action_bar" +            android:layout_width="fill_parent" +            android:layout_height="wrap_content" +            style="?attr/actionBarStyle" /> +        <com.actionbarsherlock.internal.widget.ActionBarContextView +            android:id="@+id/abs__action_context_bar" +            android:layout_width="fill_parent" +            android:layout_height="wrap_content" +            android:visibility="gone" +            style="?attr/actionModeStyle" /> +    </com.actionbarsherlock.internal.widget.ActionBarContainer> +    <ImageView android:src="?attr/windowContentOverlay" +               android:scaleType="fitXY" +               android:layout_width="match_parent" +               android:layout_height="wrap_content" +               android:layout_below="@id/abs__action_bar_container" /> +</RelativeLayout> diff --git a/libraries/ActionBarSherlock/res/layout/abs__action_bar_home.xml b/libraries/ActionBarSherlock/res/layout/abs__action_bar_home.xml new file mode 100644 index 000000000..5c1e9ec4b --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__action_bar_home.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<view xmlns:android="http://schemas.android.com/apk/res/android" +      class="com.actionbarsherlock.internal.widget.ActionBarView$HomeView" +      android:layout_width="wrap_content" +      android:layout_height="fill_parent" +      android:background="?attr/actionBarItemBackground"> +    <ImageView android:id="@id/abs__up" +               android:src="?attr/homeAsUpIndicator" +               android:layout_gravity="center_vertical|left" +               android:visibility="gone" +               android:layout_width="wrap_content" +               android:layout_height="wrap_content" +               android:layout_marginRight="-8dip" /> +    <ImageView android:id="@id/abs__home" +               android:layout_width="wrap_content" +               android:layout_height="wrap_content" +               android:layout_marginRight="8dip" +               android:layout_marginTop="@dimen/abs__action_bar_icon_vertical_padding" +               android:layout_marginBottom="@dimen/abs__action_bar_icon_vertical_padding" +               android:layout_gravity="center" +               android:adjustViewBounds="true" +               android:scaleType="fitCenter" /> +</view> diff --git a/libraries/ActionBarSherlock/res/layout/abs__action_bar_tab.xml b/libraries/ActionBarSherlock/res/layout/abs__action_bar_tab.xml new file mode 100644 index 000000000..f46f7a044 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__action_bar_tab.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> + +<view +    xmlns:android="http://schemas.android.com/apk/res/android" +    class="com.actionbarsherlock.internal.widget.ScrollingTabContainerView$TabView" +    style="?attr/actionBarTabStyle" +/>
\ No newline at end of file diff --git a/libraries/ActionBarSherlock/res/layout/abs__action_bar_tab_bar_view.xml b/libraries/ActionBarSherlock/res/layout/abs__action_bar_tab_bar_view.xml new file mode 100644 index 000000000..0d51220c9 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__action_bar_tab_bar_view.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> + +<com.actionbarsherlock.internal.widget.IcsLinearLayout +    xmlns:android="http://schemas.android.com/apk/res/android" +    style="?attr/actionBarTabBarStyle" +/>
\ No newline at end of file diff --git a/libraries/ActionBarSherlock/res/layout/abs__action_bar_title_item.xml b/libraries/ActionBarSherlock/res/layout/abs__action_bar_title_item.xml new file mode 100644 index 000000000..dd69acada --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__action_bar_title_item.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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="wrap_content" +              android:layout_height="wrap_content" +              android:orientation="horizontal" +              android:paddingRight="16dip" +              android:background="?attr/actionBarItemBackground" +              android:enabled="false"> + +    <ImageView android:id="@id/abs__up" +               android:src="?attr/homeAsUpIndicator" +               android:layout_gravity="center_vertical|left" +               android:visibility="gone" +               android:layout_width="wrap_content" +               android:layout_height="wrap_content" /> + +    <LinearLayout android:layout_width="wrap_content" +                  android:layout_height="wrap_content" +                  android:layout_gravity="center_vertical|left" +                  android:orientation="vertical"> +        <TextView android:id="@+id/abs__action_bar_title" +                  android:layout_width="wrap_content" +                  android:layout_height="wrap_content" +                  android:singleLine="true" +                  android:ellipsize="end" /> +        <TextView android:id="@+id/abs__action_bar_subtitle" +                  android:layout_width="wrap_content" +                  android:layout_height="wrap_content" +                  android:layout_marginTop="@dimen/abs__action_bar_subtitle_top_margin" +                  android:layout_marginBottom="@dimen/abs__action_bar_subtitle_bottom_margin" +                  android:singleLine="true" +                  android:ellipsize="end" +                  android:visibility="gone" /> +    </LinearLayout> +</LinearLayout> diff --git a/libraries/ActionBarSherlock/res/layout/abs__action_menu_item_layout.xml b/libraries/ActionBarSherlock/res/layout/abs__action_menu_item_layout.xml new file mode 100644 index 000000000..13149fd63 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__action_menu_item_layout.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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.actionbarsherlock.internal.view.menu.ActionMenuItemView xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="wrap_content" +    android:layout_height="wrap_content" +    android:layout_gravity="center" +    android:addStatesFromChildren="true" +    android:gravity="center" +    android:focusable="true" +    android:paddingLeft="4dip" +    android:paddingRight="4dip" +    style="?attr/actionButtonStyle"> +    <ImageButton android:id="@+id/abs__imageButton" +                 android:layout_width="wrap_content" +                 android:layout_height="wrap_content" +                 android:layout_gravity="center" +                 android:visibility="gone" +                 android:layout_marginTop="4dip" +                 android:layout_marginBottom="4dip" +                 android:layout_marginLeft="4dip" +                 android:layout_marginRight="4dip" +                 android:scaleType="fitCenter" +                 android:adjustViewBounds="true" +                 android:background="@null" +                 android:focusable="false" /> +    <com.actionbarsherlock.internal.widget.CapitalizingButton android:id="@+id/abs__textButton" +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:layout_gravity="center" +            android:visibility="gone" +            android:textAppearance="?attr/actionMenuTextAppearance" +            style="?attr/buttonStyleSmall" +            android:textColor="?attr/actionMenuTextColor" +            android:singleLine="true" +            android:ellipsize="none" +            android:background="@null" +            android:paddingTop="4dip" +            android:paddingBottom="4dip" +            android:paddingLeft="4dip" +            android:paddingRight="4dip" +            android:focusable="false" /> +</com.actionbarsherlock.internal.view.menu.ActionMenuItemView> diff --git a/libraries/ActionBarSherlock/res/layout/abs__action_menu_layout.xml b/libraries/ActionBarSherlock/res/layout/abs__action_menu_layout.xml new file mode 100644 index 000000000..a6f8e53f8 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__action_menu_layout.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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.actionbarsherlock.internal.view.menu.ActionMenuView +     xmlns:android="http://schemas.android.com/apk/res/android" +     android:layout_width="wrap_content" +     android:layout_height="wrap_content" +     android:divider="?attr/actionBarDivider" +     android:dividerPadding="12dip" +     android:gravity="center_vertical" /> diff --git a/libraries/ActionBarSherlock/res/layout/abs__action_mode_bar.xml b/libraries/ActionBarSherlock/res/layout/abs__action_mode_bar.xml new file mode 100644 index 000000000..7168dc77f --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__action_mode_bar.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2010, 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.actionbarsherlock.internal.widget.ActionBarContextView +     xmlns:android="http://schemas.android.com/apk/res/android" +     android:layout_width="fill_parent" +     android:layout_height="wrap_content" +     android:visibility="gone" +     style="?attr/actionModeStyle" /> diff --git a/libraries/ActionBarSherlock/res/layout/abs__action_mode_close_item.xml b/libraries/ActionBarSherlock/res/layout/abs__action_mode_close_item.xml new file mode 100644 index 000000000..875ec3e1b --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__action_mode_close_item.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.widget.NineLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +        android:id="@+id/abs__action_mode_close_button" +        android:focusable="true" +        android:clickable="true" +        android:paddingLeft="8dip" +        style="?attr/actionModeCloseButtonStyle" +        android:layout_width="wrap_content" +        android:layout_height="fill_parent" +        android:layout_marginRight="16dip"> +    <ImageView android:layout_width="wrap_content" +               android:layout_height="wrap_content" +               android:layout_gravity="center" +               android:scaleType="fitCenter" +               android:src="?attr/actionModeCloseDrawable" /> +</com.actionbarsherlock.internal.nineoldandroids.widget.NineLinearLayout> diff --git a/libraries/ActionBarSherlock/res/layout/abs__activity_chooser_view.xml b/libraries/ActionBarSherlock/res/layout/abs__activity_chooser_view.xml new file mode 100644 index 000000000..6a0ac9ece --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__activity_chooser_view.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +**     http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<com.actionbarsherlock.internal.widget.IcsLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:id="@+id/abs__activity_chooser_view_content" +    android:layout_width="wrap_content" +    android:layout_height="wrap_content" +    android:layout_gravity="center" +    style="?attr/activityChooserViewStyle"> + +    <FrameLayout +        android:id="@+id/abs__expand_activities_button" +        android:layout_width="wrap_content" +        android:layout_height="fill_parent" +        android:layout_gravity="center" +        android:focusable="true" +        android:addStatesFromChildren="true" +        android:background="?attr/actionBarItemBackground"> + +        <ImageView android:id="@+id/abs__image" +            android:layout_width="56dip" +            android:layout_height="36dip" +            android:layout_gravity="center" +            android:paddingTop="2dip" +            android:paddingBottom="2dip" +            android:paddingLeft="12dip" +            android:paddingRight="12dip" +            android:scaleType="fitCenter" +            android:adjustViewBounds="true" /> + +    </FrameLayout> + +    <FrameLayout +        android:id="@+id/abs__default_activity_button" +        android:layout_width="wrap_content" +        android:layout_height="fill_parent" +        android:layout_gravity="center" +        android:focusable="true" +        android:addStatesFromChildren="true" +        android:background="?attr/actionBarItemBackground"> + +        <ImageView android:id="@+id/abs__image" +            android:layout_width="56dip" +            android:layout_height="36dip" +            android:layout_gravity="center" +            android:paddingTop="2dip" +            android:paddingBottom="2dip" +            android:paddingLeft="12dip" +            android:paddingRight="12dip" +            android:scaleType="fitCenter" +            android:adjustViewBounds="true" /> + +    </FrameLayout> + +</com.actionbarsherlock.internal.widget.IcsLinearLayout> diff --git a/libraries/ActionBarSherlock/res/layout/abs__activity_chooser_view_list_item.xml b/libraries/ActionBarSherlock/res/layout/abs__activity_chooser_view_list_item.xml new file mode 100644 index 000000000..b430032a1 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__activity_chooser_view_list_item.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:id="@+id/abs__list_item" +    android:layout_width="match_parent" +    android:layout_height="?attr/dropdownListPreferredItemHeight" +    android:paddingLeft="16dip" +    android:paddingRight="16dip" +    android:minWidth="196dip" +    android:background="?attr/activatedBackgroundIndicator" +    android:orientation="vertical" > + +    <LinearLayout +        android:layout_width="wrap_content" +        android:layout_height="match_parent" +        android:duplicateParentState="true" > + +        <ImageView +            android:id="@+id/abs__icon" +            android:layout_width="32dip" +            android:layout_height="32dip" +            android:layout_gravity="center_vertical" +            android:layout_marginRight="8dip" +            android:duplicateParentState="true" /> + +        <TextView +            android:id="@+id/abs__title" +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:layout_gravity="center_vertical" +            android:textAppearance="?attr/textAppearanceLargePopupMenu" +            android:duplicateParentState="true" +            android:singleLine="true" +            android:ellipsize="marquee" +            android:fadingEdge="horizontal" /> + +    </LinearLayout> + +</LinearLayout> diff --git a/libraries/ActionBarSherlock/res/layout/abs__dialog_title_holo.xml b/libraries/ActionBarSherlock/res/layout/abs__dialog_title_holo.xml new file mode 100644 index 000000000..ab2b0ee6c --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__dialog_title_holo.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +**     http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +This is an optimized layout for a screen, with the minimum set of features +enabled. +--> + +<com.actionbarsherlock.internal.widget.FakeDialogPhoneWindow xmlns:android="http://schemas.android.com/apk/res/android" +    android:orientation="vertical" +    android:fitsSystemWindows="true"> +    <TextView android:id="@android:id/title" style="?android:attr/windowTitleStyle" +        android:layout_width="wrap_content" +        android:layout_height="wrap_content" +        android:minHeight="@dimen/abs__alert_dialog_title_height" +        android:paddingLeft="16dip" +        android:paddingRight="16dip" +        android:gravity="center_vertical|left" /> +    <View android:id="@+id/abs__titleDivider" +            android:layout_width="fill_parent" +            android:layout_height="2dip" +            android:background="@color/abs__holo_blue_light" /> +    <FrameLayout +        android:layout_width="match_parent" android:layout_height="0dp" +        android:layout_weight="1" +        android:orientation="vertical" +        android:foreground="?attr/windowContentOverlay"> +        <FrameLayout android:id="@+id/abs__content" +            android:layout_width="fill_parent" +            android:layout_height="fill_parent" /> +    </FrameLayout> +</com.actionbarsherlock.internal.widget.FakeDialogPhoneWindow> diff --git a/libraries/ActionBarSherlock/res/layout/abs__list_menu_item_checkbox.xml b/libraries/ActionBarSherlock/res/layout/abs__list_menu_item_checkbox.xml new file mode 100644 index 000000000..39aca3a8d --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__list_menu_item_checkbox.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2007 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. +--> + +<CheckBox xmlns:android="http://schemas.android.com/apk/res/android" +    android:id="@+id/abs__checkbox" +    android:layout_width="wrap_content" +    android:layout_height="wrap_content" +    android:layout_gravity="center_vertical" +    android:focusable="false" +    android:clickable="false" +    android:duplicateParentState="true" /> + + diff --git a/libraries/ActionBarSherlock/res/layout/abs__list_menu_item_icon.xml b/libraries/ActionBarSherlock/res/layout/abs__list_menu_item_icon.xml new file mode 100644 index 000000000..55ab28a24 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__list_menu_item_icon.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2007 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. +--> + +<ImageView xmlns:android="http://schemas.android.com/apk/res/android" +    android:id="@+id/abs__icon" +    android:layout_width="wrap_content" +    android:layout_height="wrap_content" +    android:layout_gravity="center_vertical" +    android:layout_marginLeft="8dip" +    android:layout_marginRight="-8dip" +    android:layout_marginTop="8dip" +    android:layout_marginBottom="8dip" +    android:scaleType="centerInside" +    android:duplicateParentState="true" /> + diff --git a/libraries/ActionBarSherlock/res/layout/abs__list_menu_item_layout.xml b/libraries/ActionBarSherlock/res/layout/abs__list_menu_item_layout.xml new file mode 100644 index 000000000..147f36fe8 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__list_menu_item_layout.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2007 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.actionbarsherlock.internal.view.menu.ListMenuItemView xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="fill_parent" +    android:layout_height="?attr/listPreferredItemHeightSmall"> +     +    <!-- Icon will be inserted here. --> +     +    <!-- The title and summary have some gap between them, and this 'group' should be centered vertically. --> +    <RelativeLayout +        android:layout_width="0dip" +        android:layout_weight="1" +        android:layout_height="wrap_content" +        android:layout_gravity="center_vertical" +        android:layout_marginLeft="?attr/listPreferredItemPaddingLeft" +        android:layout_marginRight="?attr/listPreferredItemPaddingRight" +        android:duplicateParentState="true"> +         +        <TextView  +            android:id="@+id/abs__title" +            android:layout_width="fill_parent" +            android:layout_height="wrap_content" +            android:layout_alignParentTop="true" +            android:layout_alignParentLeft="true" +            android:textAppearance="?attr/textAppearanceListItemSmall" +            android:singleLine="true" +            android:duplicateParentState="true" +            android:ellipsize="marquee" +            android:fadingEdge="horizontal" /> + +        <TextView +            android:id="@+id/abs__shortcut" +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:layout_below="@id/abs__title" +            android:layout_alignParentLeft="true" +            android:textAppearance="?attr/textAppearanceSmall" +            android:singleLine="true" +            android:duplicateParentState="true" /> + +    </RelativeLayout> + +    <!-- Checkbox, and/or radio button will be inserted here. --> +     +</com.actionbarsherlock.internal.view.menu.ListMenuItemView> diff --git a/libraries/ActionBarSherlock/res/layout/abs__list_menu_item_radio.xml b/libraries/ActionBarSherlock/res/layout/abs__list_menu_item_radio.xml new file mode 100644 index 000000000..ff54bbecd --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__list_menu_item_radio.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2007 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. +--> + +<RadioButton xmlns:android="http://schemas.android.com/apk/res/android" +    android:id="@+id/abs__radio" +    android:layout_width="wrap_content" +    android:layout_height="wrap_content" +    android:layout_gravity="center_vertical" +    android:focusable="false" +    android:clickable="false" +    android:duplicateParentState="true" /> diff --git a/libraries/ActionBarSherlock/res/layout/abs__popup_menu_item_layout.xml b/libraries/ActionBarSherlock/res/layout/abs__popup_menu_item_layout.xml new file mode 100644 index 000000000..d42425ad3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__popup_menu_item_layout.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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.actionbarsherlock.internal.view.menu.ListMenuItemView xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="fill_parent" +    android:layout_height="?attr/dropdownListPreferredItemHeight" +    android:minWidth="196dip" +    android:paddingRight="16dip"> +     +    <!-- Icon will be inserted here. --> +     +    <!-- The title and summary have some gap between them, and this 'group' should be centered vertically. --> +    <RelativeLayout +        android:layout_width="0dip" +        android:layout_weight="1" +        android:layout_height="wrap_content" +        android:layout_gravity="center_vertical" +        android:layout_marginLeft="16dip" +        android:duplicateParentState="true"> +         +        <TextView  +            android:id="@+id/abs__title" +            android:layout_width="fill_parent" +            android:layout_height="wrap_content" +            android:layout_alignParentTop="true" +            android:layout_alignParentLeft="true" +            android:textAppearance="?attr/textAppearanceLargePopupMenu" +            android:singleLine="true" +            android:duplicateParentState="true" +            android:ellipsize="marquee" +            android:fadingEdge="horizontal" /> + +        <TextView +            android:id="@+id/abs__shortcut" +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:layout_below="@id/abs__title" +            android:layout_alignParentLeft="true" +            android:textAppearance="?attr/textAppearanceSmallPopupMenu" +            android:singleLine="true" +            android:duplicateParentState="true" /> + +    </RelativeLayout> + +    <!-- Checkbox, and/or radio button will be inserted here. --> +     +</com.actionbarsherlock.internal.view.menu.ListMenuItemView> diff --git a/libraries/ActionBarSherlock/res/layout/abs__screen_action_bar.xml b/libraries/ActionBarSherlock/res/layout/abs__screen_action_bar.xml new file mode 100644 index 000000000..1fb82fe9a --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__screen_action_bar.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<!-- +This is an optimized layout for a screen with the Action Bar enabled. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="match_parent" +    android:layout_height="match_parent" +    android:orientation="vertical" +    android:fitsSystemWindows="true"> +    <com.actionbarsherlock.internal.widget.ActionBarContainer +        android:id="@+id/abs__action_bar_container" +        android:layout_width="fill_parent" +        android:layout_height="wrap_content" +        style="?attr/actionBarStyle"> +        <com.actionbarsherlock.internal.widget.ActionBarView +            android:id="@+id/abs__action_bar" +            android:layout_width="fill_parent" +            android:layout_height="wrap_content" +            style="?attr/actionBarStyle" /> +        <com.actionbarsherlock.internal.widget.ActionBarContextView +            android:id="@+id/abs__action_context_bar" +            android:layout_width="fill_parent" +            android:layout_height="wrap_content" +            android:visibility="gone" +            style="?attr/actionModeStyle" /> +    </com.actionbarsherlock.internal.widget.ActionBarContainer> +    <com.actionbarsherlock.internal.nineoldandroids.widget.NineFrameLayout +        android:id="@+id/abs__content" +        android:layout_width="fill_parent" +        android:layout_height="0dip" +        android:layout_weight="1" +        android:foregroundGravity="fill_horizontal|top" +        android:foreground="?attr/windowContentOverlay" /> +    <com.actionbarsherlock.internal.widget.ActionBarContainer +        android:id="@+id/abs__split_action_bar" +        android:layout_width="fill_parent" +        android:layout_height="wrap_content" +        android:visibility="gone" +        android:gravity="center" +        style="?attr/actionBarSplitStyle" /> +</LinearLayout> diff --git a/libraries/ActionBarSherlock/res/layout/abs__screen_action_bar_overlay.xml b/libraries/ActionBarSherlock/res/layout/abs__screen_action_bar_overlay.xml new file mode 100644 index 000000000..0961ef561 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__screen_action_bar_overlay.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<!-- +This is an optimized layout for a screen with +the Action Bar enabled overlaying application content. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="match_parent" +    android:layout_height="match_parent" +    android:fitsSystemWindows="true"> +    <com.actionbarsherlock.internal.nineoldandroids.widget.NineFrameLayout android:id="@+id/abs__content" +        android:layout_width="match_parent" +        android:layout_height="match_parent" /> +    <com.actionbarsherlock.internal.widget.ActionBarContainer android:id="@+id/abs__action_bar_container" +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        android:layout_alignParentTop="true" +        style="?attr/actionBarStyle" +        android:gravity="top"> +        <com.actionbarsherlock.internal.widget.ActionBarView +            android:id="@+id/abs__action_bar" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            style="?attr/actionBarStyle" /> +        <com.actionbarsherlock.internal.widget.ActionBarContextView +            android:id="@+id/abs__action_context_bar" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:visibility="gone" +            style="?attr/actionModeStyle" /> +    </com.actionbarsherlock.internal.widget.ActionBarContainer> +    <ImageView android:src="?attr/windowContentOverlay" +               android:scaleType="fitXY" +               android:layout_width="match_parent" +               android:layout_height="wrap_content" +               android:layout_below="@id/abs__action_bar_container" /> +    <com.actionbarsherlock.internal.widget.ActionBarContainer android:id="@+id/abs__split_action_bar" +                  android:layout_width="match_parent" +                  android:layout_height="wrap_content" +                  android:layout_gravity="bottom" +                  style="?attr/actionBarSplitStyle" +                  android:visibility="gone" +                  android:gravity="center"/> +</FrameLayout> diff --git a/libraries/ActionBarSherlock/res/layout/abs__screen_simple.xml b/libraries/ActionBarSherlock/res/layout/abs__screen_simple.xml new file mode 100644 index 000000000..33e2dea0d --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__screen_simple.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/layout/screen_simple.xml +** +** Copyright 2006, 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. +*/ + +This is an optimized layout for a screen, with the minimum set of features +enabled. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:fitsSystemWindows="true" +    android:orientation="vertical"> +    <ViewStub android:id="@+id/abs__action_mode_bar_stub" +              android:inflatedId="@+id/abs__action_mode_bar" +              android:layout="@layout/abs__action_mode_bar" +              android:layout_width="fill_parent" +              android:layout_height="wrap_content" /> +    <com.actionbarsherlock.internal.nineoldandroids.widget.NineFrameLayout +         android:id="@+id/abs__content" +         android:layout_width="fill_parent" +         android:layout_height="fill_parent" +         android:foregroundGravity="fill_horizontal|top" +         android:foreground="?attr/windowContentOverlay" /> +</LinearLayout> diff --git a/libraries/ActionBarSherlock/res/layout/abs__screen_simple_overlay_action_mode.xml b/libraries/ActionBarSherlock/res/layout/abs__screen_simple_overlay_action_mode.xml new file mode 100644 index 000000000..f8b9fb185 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__screen_simple_overlay_action_mode.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +**     http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +This is an optimized layout for a screen, with the minimum set of features +enabled. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="match_parent" +    android:layout_height="match_parent" +    android:fitsSystemWindows="true"> +    <com.actionbarsherlock.internal.nineoldandroids.widget.NineFrameLayout +         android:id="@+id/abs__content" +         android:layout_width="fill_parent" +         android:layout_height="fill_parent" +         android:foregroundGravity="fill_horizontal|top" +         android:foreground="?attr/windowContentOverlay" /> +    <ViewStub android:id="@+id/abs__action_mode_bar_stub" +              android:inflatedId="@+id/abs__action_mode_bar" +              android:layout="@layout/abs__action_mode_bar" +              android:layout_width="fill_parent" +              android:layout_height="wrap_content" /> +</FrameLayout> diff --git a/libraries/ActionBarSherlock/res/layout/abs__search_dropdown_item_icons_2line.xml b/libraries/ActionBarSherlock/res/layout/abs__search_dropdown_item_icons_2line.xml new file mode 100644 index 000000000..e1d3dc49c --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__search_dropdown_item_icons_2line.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +**     http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +                android:paddingLeft="@dimen/abs__dropdownitem_text_padding_left" +                android:paddingRight="4dip" +                android:layout_width="match_parent" +                android:layout_height="?attr/searchResultListItemHeight" > + +    <!-- Icons come first in the layout, since their placement doesn't depend on +         the placement of the text views. --> +    <ImageView android:id="@android:id/icon1" +               android:layout_width="@dimen/abs__dropdownitem_icon_width" +               android:layout_height="48dip" +               android:scaleType="centerInside" +               android:layout_alignParentLeft="true" +               android:layout_alignParentTop="true" +               android:layout_alignParentBottom="true" +               android:visibility="invisible" /> + +    <ImageView android:id="@+id/edit_query" +               android:layout_width="48dip" +               android:layout_height="48dip" +               android:scaleType="centerInside" +               android:layout_alignParentRight="true" +               android:layout_alignParentTop="true" +               android:layout_alignParentBottom="true" +               android:src="?attr/searchViewEditQuery" +               android:background="?attr/searchViewEditQueryBackground" +               android:visibility="gone" /> + +    <ImageView android:id="@android:id/icon2" +               android:layout_width="48dip" +               android:layout_height="48dip" +               android:scaleType="centerInside" +               android:layout_alignWithParentIfMissing="true" +               android:layout_toLeftOf="@id/edit_query" +               android:layout_alignParentTop="true" +               android:layout_alignParentBottom="true" +               android:visibility="gone" /> + + +    <!-- The subtitle comes before the title, since the height of the title depends on whether the +         subtitle is visible or gone. --> +    <TextView android:id="@android:id/text2" +              style="?android:attr/dropDownItemStyle" +              android:textAppearance="?attr/textAppearanceSearchResultSubtitle" +              android:singleLine="true" +              android:layout_width="match_parent" +              android:layout_height="29dip" +              android:paddingBottom="4dip" +              android:gravity="top" +              android:layout_toRightOf="@android:id/icon1" +              android:layout_toLeftOf="@android:id/icon2" +              android:layout_alignWithParentIfMissing="true" +              android:layout_alignParentBottom="true" +              android:visibility="gone" /> + +    <!-- The title is placed above the subtitle, if there is one. If there is no +         subtitle, it fills the parent. --> +    <TextView android:id="@android:id/text1" +              style="?android:attr/dropDownItemStyle" +              android:textAppearance="?attr/textAppearanceSearchResultTitle" +              android:singleLine="true" +              android:layout_width="match_parent" +              android:layout_height="wrap_content" +              android:layout_centerVertical="true" +              android:layout_toRightOf="@android:id/icon1" +              android:layout_toLeftOf="@android:id/icon2" +              android:layout_above="@android:id/text2" /> + +</RelativeLayout> diff --git a/libraries/ActionBarSherlock/res/layout/abs__search_view.xml b/libraries/ActionBarSherlock/res/layout/abs__search_view.xml new file mode 100644 index 000000000..6ba319121 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__search_view.xml @@ -0,0 +1,159 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (C) 2010 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/abs__search_bar" +        android:layout_width="match_parent" +        android:layout_height="match_parent" +        android:orientation="horizontal" +        > + +    <!-- This is actually used for the badge icon *or* the badge label (or neither) --> +    <TextView +            android:id="@+id/abs__search_badge" +            android:layout_width="wrap_content" +            android:layout_height="match_parent" +            android:gravity="center_vertical" +            android:layout_marginBottom="2dip" +            android:drawablePadding="0dip" +            android:textAppearance="?android:attr/textAppearanceMedium" +            android:textColor="?attr/textColorPrimary" +            android:visibility="gone" +            /> + +    <ImageView +            android:id="@+id/abs__search_button" +            style="?android:attr/actionButtonStyle" +            android:layout_width="wrap_content" +            android:layout_height="match_parent" +            android:layout_gravity="center_vertical" +            android:src="?attr/searchViewSearchIcon" +            android:contentDescription="@string/abs__searchview_description_search" +            /> + +    <LinearLayout +            android:id="@+id/abs__search_edit_frame" +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:layout_weight="1" +            android:layout_gravity="center_vertical" +            android:layout_marginTop="4dip" +            android:layout_marginBottom="4dip" +            android:layout_marginLeft="8dip" +            android:layout_marginRight="8dip" +            android:orientation="horizontal"> + +        <ImageView +                android:id="@+id/abs__search_mag_icon" +                android:layout_width="@dimen/abs__dropdownitem_icon_width" +                android:layout_height="wrap_content" +                android:scaleType="centerInside" +                android:layout_marginLeft="@dimen/abs__dropdownitem_text_padding_left" +                android:layout_gravity="center_vertical" +                android:src="?attr/searchViewSearchIcon" +                android:visibility="gone" +                /> + +        <!-- Inner layout contains the app icon, button(s) and EditText --> +        <LinearLayout +                android:id="@+id/abs__search_plate" +                android:layout_width="wrap_content" +                android:layout_height="wrap_content" +                android:layout_weight="1" +                android:layout_gravity="center_vertical" +                android:orientation="horizontal" +                android:background="?attr/searchViewTextField"> + +            <view class="com.actionbarsherlock.widget.SearchView$SearchAutoComplete" +                  style="?attr/searchAutoCompleteTextView" +                  android:id="@+id/abs__search_src_text" +                  android:layout_height="36dip" +                  android:layout_width="0dp" +                  android:layout_weight="1" +                  android:minWidth="@dimen/abs__search_view_text_min_width" +                  android:layout_gravity="bottom" +                  android:paddingLeft="@dimen/abs__dropdownitem_text_padding_left" +                  android:paddingRight="@dimen/abs__dropdownitem_text_padding_right" +                  android:singleLine="true" +                  android:ellipsize="end" +                  android:background="@null" +                  android:inputType="text|textAutoComplete|textNoSuggestions" +                  android:imeOptions="actionSearch" +                  android:dropDownHeight="wrap_content" +                  android:dropDownAnchor="@id/abs__search_edit_frame" +                  android:dropDownVerticalOffset="0dip" +                  android:dropDownHorizontalOffset="0dip" +                  android:contentDescription="@string/abs__searchview_description_query" +                  /> + +            <ImageView +                    android:id="@+id/abs__search_close_btn" +                    android:layout_width="wrap_content" +                    android:layout_height="match_parent" +                    android:paddingLeft="8dip" +                    android:paddingRight="8dip" +                    android:layout_gravity="center_vertical" +                    android:background="?attr/selectableItemBackground" +                    android:src="?attr/searchViewCloseIcon" +                    android:focusable="true" +                    android:contentDescription="@string/abs__searchview_description_clear" +                    /> + +        </LinearLayout> + +        <LinearLayout +                android:id="@+id/abs__submit_area" +                android:orientation="horizontal" +                android:layout_width="wrap_content" +                android:layout_height="match_parent" +                android:background="?attr/searchViewTextFieldRight"> + +            <ImageView +                    android:id="@+id/abs__search_go_btn" +                    android:layout_width="wrap_content" +                    android:layout_height="match_parent" +                    android:layout_gravity="center_vertical" +                    android:paddingLeft="16dip" +                    android:paddingRight="16dip" +                    android:background="?attr/selectableItemBackground" +                    android:src="?attr/searchViewGoIcon" +                    android:visibility="gone" +                    android:focusable="true" +                    android:contentDescription="@string/abs__searchview_description_submit" +                    /> + +            <ImageView +                    android:id="@+id/abs__search_voice_btn" +                    android:layout_width="wrap_content" +                    android:layout_height="match_parent" +                    android:layout_gravity="center_vertical" +                    android:paddingLeft="16dip" +                    android:paddingRight="16dip" +                    android:src="?attr/searchViewVoiceIcon" +                    android:background="?attr/selectableItemBackground" +                    android:visibility="gone" +                    android:focusable="true" +                    android:contentDescription="@string/abs__searchview_description_voice" +                    /> + +        </LinearLayout> +    </LinearLayout> + +</LinearLayout> diff --git a/libraries/ActionBarSherlock/res/layout/abs__simple_dropdown_hint.xml b/libraries/ActionBarSherlock/res/layout/abs__simple_dropdown_hint.xml new file mode 100644 index 000000000..8fc0eb12c --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/abs__simple_dropdown_hint.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +**     http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" +          android:id="@android:id/text1" +          android:textAppearance="?android:attr/dropDownHintAppearance" +          android:singleLine="true" +          android:layout_marginLeft="3dip" +          android:layout_marginTop="3dip" +          android:layout_marginRight="3dip" +          android:layout_marginBottom="3dip" +          android:layout_width="match_parent" +          android:layout_height="wrap_content" /> diff --git a/libraries/ActionBarSherlock/res/layout/sherlock_spinner_dropdown_item.xml b/libraries/ActionBarSherlock/res/layout/sherlock_spinner_dropdown_item.xml new file mode 100644 index 000000000..a6c6252d2 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/sherlock_spinner_dropdown_item.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/layout/simple_spinner_item.xml +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License");  +** you may not use this file except in compliance with the License.  +** You may obtain a copy of the License at  +** +**     http://www.apache.org/licenses/LICENSE-2.0  +** +** Unless required by applicable law or agreed to in writing, software  +** distributed under the License is distributed on an "AS IS" BASIS,  +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  +** See the License for the specific language governing permissions and  +** limitations under the License. +*/ +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android"  +    android:id="@android:id/text1" +    style="?attr/spinnerDropDownItemStyle" +    android:singleLine="true" +    android:layout_width="match_parent" +    android:layout_height="?attr/dropdownListPreferredItemHeight" +    android:ellipsize="marquee" /> diff --git a/libraries/ActionBarSherlock/res/layout/sherlock_spinner_item.xml b/libraries/ActionBarSherlock/res/layout/sherlock_spinner_item.xml new file mode 100644 index 000000000..bea740178 --- /dev/null +++ b/libraries/ActionBarSherlock/res/layout/sherlock_spinner_item.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/layout/simple_spinner_item.xml +** +** Copyright 2006, 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. +*/ +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android"  +    android:id="@android:id/text1" +    style="?attr/spinnerItemStyle" +    android:singleLine="true" +    android:layout_width="match_parent" +    android:layout_height="wrap_content" +    android:ellipsize="marquee" /> diff --git a/libraries/ActionBarSherlock/res/values-land/abs__dimens.xml b/libraries/ActionBarSherlock/res/values-land/abs__dimens.xml new file mode 100644 index 000000000..502cc16a3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-land/abs__dimens.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/dimens.xml +** +** Copyright 2006, 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> +    <!-- Default height of an action bar. --> +    <dimen name="abs__action_bar_default_height">40dip</dimen> +    <!-- Vertical padding around action bar icons. --> +    <dimen name="abs__action_bar_icon_vertical_padding">4dip</dimen> +    <!-- Text size for action bar titles --> +    <dimen name="abs__action_bar_title_text_size">16dp</dimen> +    <!-- Text size for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_text_size">12dp</dimen> +    <!-- Top margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_top_margin">-2dp</dimen> +    <!-- Bottom margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_bottom_margin">4dip</dimen> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-large-hdpi-1024x600/abs__dimens.xml b/libraries/ActionBarSherlock/res/values-large-hdpi-1024x600/abs__dimens.xml new file mode 100644 index 000000000..3312cfa7f --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-large-hdpi-1024x600/abs__dimens.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/dimens.xml +** +** Copyright 2006, 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> +    <!-- Default height of an action bar. --> +    <dimen name="abs__action_bar_default_height">48dip</dimen> +    <!-- Vertical padding around action bar icons. --> +    <dimen name="abs__action_bar_icon_vertical_padding">8dip</dimen> +    <!-- Text size for action bar titles --> +    <dimen name="abs__action_bar_title_text_size">18dp</dimen> +    <!-- Text size for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_text_size">14dp</dimen> +    <!-- Top margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_top_margin">-3dp</dimen> +    <!-- Bottom margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_bottom_margin">5dip</dimen> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-large-land-hdpi-1024x600/abs__dimens.xml b/libraries/ActionBarSherlock/res/values-large-land-hdpi-1024x600/abs__dimens.xml new file mode 100644 index 000000000..502cc16a3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-large-land-hdpi-1024x600/abs__dimens.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/dimens.xml +** +** Copyright 2006, 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> +    <!-- Default height of an action bar. --> +    <dimen name="abs__action_bar_default_height">40dip</dimen> +    <!-- Vertical padding around action bar icons. --> +    <dimen name="abs__action_bar_icon_vertical_padding">4dip</dimen> +    <!-- Text size for action bar titles --> +    <dimen name="abs__action_bar_title_text_size">16dp</dimen> +    <!-- Text size for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_text_size">12dp</dimen> +    <!-- Top margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_top_margin">-2dp</dimen> +    <!-- Bottom margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_bottom_margin">4dip</dimen> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-large-land-mdpi-1024x600/abs__dimens.xml b/libraries/ActionBarSherlock/res/values-large-land-mdpi-1024x600/abs__dimens.xml new file mode 100644 index 000000000..3312cfa7f --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-large-land-mdpi-1024x600/abs__dimens.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/dimens.xml +** +** Copyright 2006, 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> +    <!-- Default height of an action bar. --> +    <dimen name="abs__action_bar_default_height">48dip</dimen> +    <!-- Vertical padding around action bar icons. --> +    <dimen name="abs__action_bar_icon_vertical_padding">8dip</dimen> +    <!-- Text size for action bar titles --> +    <dimen name="abs__action_bar_title_text_size">18dp</dimen> +    <!-- Text size for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_text_size">14dp</dimen> +    <!-- Top margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_top_margin">-3dp</dimen> +    <!-- Bottom margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_bottom_margin">5dip</dimen> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-large-mdpi-1024x600/abs__dimens.xml b/libraries/ActionBarSherlock/res/values-large-mdpi-1024x600/abs__dimens.xml new file mode 100644 index 000000000..35910333b --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-large-mdpi-1024x600/abs__dimens.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/dimens.xml +** +** Copyright 2006, 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> +    <!-- Default height of an action bar. --> +    <dimen name="abs__action_bar_default_height">56dip</dimen> +    <!-- Vertical padding around action bar icons. --> +    <dimen name="abs__action_bar_icon_vertical_padding">4dip</dimen> +    <!-- Text size for action bar titles --> +    <dimen name="abs__action_bar_title_text_size">18dp</dimen> +    <!-- Text size for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_text_size">14dp</dimen> +    <!-- Top margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_top_margin">-3dp</dimen> +    <!-- Bottom margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_bottom_margin">9dip</dimen> + +    <!-- Minimum width for an action button in the menu area of an action bar --> +    <dimen name="action_button_min_width">64dip</dimen> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-large/abs__dimens.xml b/libraries/ActionBarSherlock/res/values-large/abs__dimens.xml new file mode 100644 index 000000000..63b12f7f3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-large/abs__dimens.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/dimens.xml +** +** Copyright 2006, 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> +    <!-- The platform's desired minimum size for a dialog's width when it +         is along the major axis (that is the screen is landscape).  This may +         be either a fraction or a dimension. --> +    <item type="dimen" name="abs__dialog_min_width_major">55%</item> +    <!-- The platform's desired minimum size for a dialog's width when it +         is along the minor axis (that is the screen is portrait).  This may +         be either a fraction or a dimension. --> +    <item type="dimen" name="abs__dialog_min_width_minor">80%</item> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-sw600dp/abs__bools.xml b/libraries/ActionBarSherlock/res/values-sw600dp/abs__bools.xml new file mode 100644 index 000000000..7a48e1542 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-sw600dp/abs__bools.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<resources> +    <bool name="abs__action_bar_expanded_action_views_exclusive">false</bool> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-sw600dp/abs__dimens.xml b/libraries/ActionBarSherlock/res/values-sw600dp/abs__dimens.xml new file mode 100644 index 000000000..f67853817 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-sw600dp/abs__dimens.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/dimens.xml +** +** Copyright 2006, 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> +    <!-- Default height of an action bar. --> +    <dimen name="abs__action_bar_default_height">56dip</dimen> +    <!-- Vertical padding around action bar icons. --> +    <dimen name="abs__action_bar_icon_vertical_padding">4dip</dimen> +    <!-- Text size for action bar titles --> +    <dimen name="abs__action_bar_title_text_size">18dp</dimen> +    <!-- Text size for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_text_size">14dp</dimen> +    <!-- Top margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_top_margin">-3dp</dimen> +    <!-- Bottom margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_bottom_margin">9dip</dimen> +     +    <integer name="abs__max_action_buttons">5</integer> + +    <!-- Minimum width for an action button in the menu area of an action bar --> +    <dimen name="action_button_min_width">64dip</dimen> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-v11/abs__themes.xml b/libraries/ActionBarSherlock/res/values-v11/abs__themes.xml new file mode 100644 index 000000000..03473572c --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-v11/abs__themes.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> +    <style name="Sherlock.__Theme" parent="android:Theme.Holo"> +        <item name="android:windowNoTitle">true</item> +        <item name="android:windowActionBar">false</item> +    </style> +    <style name="Sherlock.__Theme.Light" parent="android:Theme.Holo.Light"> +        <item name="android:windowNoTitle">true</item> +        <item name="android:windowActionBar">false</item> +    </style> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-v14/abs__styles.xml b/libraries/ActionBarSherlock/res/values-v14/abs__styles.xml new file mode 100644 index 000000000..88a60dd92 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-v14/abs__styles.xml @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> +    <style name="Widget.Sherlock.ActionBar" parent="android:Widget.Holo.ActionBar"> +    </style> +    <style name="Widget.Sherlock.ActionBar.Solid" parent="android:Widget.Holo.ActionBar.Solid"> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar" parent="android:Widget.Holo.Light.ActionBar"> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.Solid" parent="android:Widget.Holo.Light.ActionBar.Solid"> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.Solid.Inverse" parent="android:Widget.Holo.Light.ActionBar.Solid.Inverse"> +    </style> + +    <style name="Widget.Sherlock.ActionBar.TabView" parent="android:Widget.Holo.ActionBar.TabView"> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.TabView" parent="android:Widget.Holo.Light.ActionBar.TabView"> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.TabView.Inverse" parent="android:Widget.Holo.Light.ActionBar.TabView.Inverse"> +    </style> + +    <style name="Widget.Sherlock.ActionBar.TabBar" parent="android:Widget.Holo.ActionBar.TabBar"> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.TabBar" parent="android:Widget.Holo.Light.ActionBar.TabBar"> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.TabBar.Inverse" parent="android:Widget.Holo.Light.ActionBar.TabBar.Inverse"> +    </style> + +    <style name="Widget.Sherlock.ActionBar.TabText" parent="android:Widget.Holo.ActionBar.TabText"> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.TabText" parent="android:Widget.Holo.Light.ActionBar.TabText"> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.TabText.Inverse" parent="android:Widget.Holo.Light.ActionBar.TabText.Inverse"> +    </style> + +    <style name="Widget.Sherlock.ActionButton" parent="android:Widget.Holo.ActionButton"> +    </style> +    <style name="Widget.Sherlock.Light.ActionButton" parent="android:Widget.Holo.Light.ActionButton"> +    </style> + +    <style name="Widget.Sherlock.ActionButton.CloseMode" parent="android:Widget.Holo.ActionButton.CloseMode"> +    </style> +    <style name="Widget.Sherlock.Light.ActionButton.CloseMode" parent="android:Widget.Holo.Light.ActionButton.CloseMode"> +    </style> + +    <style name="Widget.Sherlock.ActionButton.Overflow" parent="android:Widget.Holo.ActionButton.Overflow"> +    </style> +    <style name="Widget.Sherlock.Light.ActionButton.Overflow" parent="android:Widget.Holo.Light.ActionButton.Overflow"> +    </style> + +    <style name="Widget.Sherlock.ActionMode" parent="android:Widget.Holo.ActionMode"> +    </style> +    <style name="Widget.Sherlock.Light.ActionMode" parent="android:Widget.Holo.Light.ActionMode"> +    </style> +    <style name="Widget.Sherlock.Light.ActionMode.Inverse" parent="android:Widget.Holo.Light.ActionMode.Inverse"> +    </style> + +    <style name="Widget.Sherlock.PopupMenu" parent="android:Widget.Holo.PopupMenu"> +    </style> +    <style name="Widget.Sherlock.Light.PopupMenu" parent="android:Widget.Holo.Light.PopupMenu"> +    </style> + +    <style name="Widget.Sherlock.Spinner.DropDown.ActionBar" parent="android:Widget.Holo.Spinner"> +    </style> +    <style name="Widget.Sherlock.Light.Spinner.DropDown.ActionBar" parent="android:Widget.Holo.Light.Spinner"> +    </style> + +    <style name="Widget.Sherlock.ListView.DropDown" parent="android:Widget.Holo.ListView.DropDown"> +    </style> +    <style name="Widget.Sherlock.Light.ListView.DropDown" parent="android:Widget.Holo.Light.ListView.DropDown"> +    </style> + +    <style name="Widget.Sherlock.PopupWindow.ActionMode" parent="android:Widget.Holo.PopupWindow"> +    </style> +    <style name="Widget.Sherlock.Light.PopupWindow.ActionMode" parent="android:Widget.Holo.Light.PopupWindow"> +    </style> + +    <style name="Widget.Sherlock.ProgressBar" parent="android:Widget.Holo.ProgressBar"> +    </style> +    <style name="Widget.Sherlock.Light.ProgressBar" parent="android:Widget.Holo.Light.ProgressBar"> +    </style> + +    <style name="Widget.Sherlock.ProgressBar.Horizontal" parent="android:Widget.Holo.ProgressBar.Horizontal"> +    </style> +    <style name="Widget.Sherlock.Light.ProgressBar.Horizontal" parent="android:Widget.Holo.Light.ProgressBar.Horizontal"> +    </style> + +    <style name="Widget.Sherlock.SearchAutoCompleteTextView" parent="android:Widget.Holo.AutoCompleteTextView"> +    </style> +    <style name="Widget.Sherlock.Light.SearchAutoCompleteTextView" parent="android:Widget.Holo.Light.AutoCompleteTextView"> +    </style> + +    <style name="TextAppearance.Sherlock.Widget.ActionBar.Menu" parent="android:TextAppearance.Holo.Widget.ActionBar.Menu"> +    </style> + +    <style name="TextAppearance.Sherlock.Widget.ActionBar.Title" parent="android:TextAppearance.Holo.Widget.ActionBar.Title"> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionBar.Title.Inverse" parent="android:TextAppearance.Holo.Widget.ActionBar.Title.Inverse"> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionBar.Subtitle" parent="android:TextAppearance.Holo.Widget.ActionBar.Subtitle"> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionBar.Subtitle.Inverse" parent="android:TextAppearance.Holo.Widget.ActionBar.Subtitle.Inverse"> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionMode.Title" parent="android:TextAppearance.Holo.Widget.ActionMode.Title"> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionMode.Title.Inverse" parent="android:TextAppearance.Holo.Widget.ActionMode.Title.Inverse"> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionMode.Subtitle" parent="android:TextAppearance.Holo.Widget.ActionMode.Subtitle"> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionMode.Subtitle.Inverse" parent="android:TextAppearance.Holo.Widget.ActionMode.Subtitle.Inverse"> +    </style> + +    <style name="TextAppearance.Sherlock.Widget.PopupMenu" parent="android:TextAppearance.Holo.Widget.PopupMenu"> +    </style> +    <style name="TextAppearance.Sherlock.Widget.PopupMenu.Large" parent="android:TextAppearance.Holo.Widget.PopupMenu.Large"> +    </style> +    <style name="TextAppearance.Sherlock.Light.Widget.PopupMenu.Large" parent="android:TextAppearance.Holo.Widget.PopupMenu.Large"> +    </style> +    <style name="TextAppearance.Sherlock.Widget.PopupMenu.Small" parent="android:TextAppearance.Holo.Widget.PopupMenu.Small"> +    </style> +    <style name="TextAppearance.Sherlock.Light.Widget.PopupMenu.Small" parent="android:TextAppearance.Holo.Widget.PopupMenu.Small"> +    </style> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-v14/abs__themes.xml b/libraries/ActionBarSherlock/res/values-v14/abs__themes.xml new file mode 100644 index 000000000..5fac1ab58 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-v14/abs__themes.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> +    <style name="Sherlock.__Theme" parent="android:Theme.Holo"> +    </style> +    <style name="Sherlock.__Theme.Light" parent="android:Theme.Holo.Light"> +    </style> +    <style name="Sherlock.__Theme.DarkActionBar" parent="android:Theme.Holo.Light.DarkActionBar"> +        <!-- Useful for offsetting contents with an overlay action bar. --> +        <item name="actionBarSize">?android:attr/actionBarSize</item> +        <!-- Needed for our bug-fix dropdown list navigation layout. :( --> +        <item name="dropdownListPreferredItemHeight">48dp</item> +        <!-- Needed for our ShareActionProvider and SearchView implementation. --> +        <item name="android:actionBarWidgetTheme">@style/Theme.Sherlock</item> +        <!-- For crazy people who use IcsSpinner. --> +        <item name="dropDownListViewStyle">?android:attr/dropDownListViewStyle</item> +        <!-- Needed for our SearchView implementation. --> +        <item name="selectableItemBackground">?android:attr/selectableItemBackground</item> +    </style> + +    <style name="Theme.Sherlock.NoActionBar"> +      <item name="android:windowActionBar">false</item> +      <item name="android:windowNoTitle">true</item> +    </style> +    <style name="Theme.Sherlock.Light.NoActionBar"> +      <item name="android:windowActionBar">false</item> +      <item name="android:windowNoTitle">true</item> +    </style> + +    <style name="Theme.Sherlock.Dialog" parent="android:Theme.Holo.Dialog"> +    </style> +    <style name="Theme.Sherlock.Light.Dialog" parent="android:Theme.Holo.Light.Dialog"> +    </style> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-w360dp/abs__dimens.xml b/libraries/ActionBarSherlock/res/values-w360dp/abs__dimens.xml new file mode 100644 index 000000000..6f49d7e47 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-w360dp/abs__dimens.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/dimens.xml +** +** Copyright 2006, 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="abs__max_action_buttons">3</integer> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-w480dp/abs__bools.xml b/libraries/ActionBarSherlock/res/values-w480dp/abs__bools.xml new file mode 100644 index 000000000..3eaf4aee9 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-w480dp/abs__bools.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +**     http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> +    <bool name="abs__action_bar_embed_tabs">true</bool> +    <bool name="abs__split_action_bar_is_narrow">false</bool> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-w480dp/abs__config.xml b/libraries/ActionBarSherlock/res/values-w480dp/abs__config.xml new file mode 100644 index 000000000..88357b0a7 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-w480dp/abs__config.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2009, 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. +*/ +--> + +<!-- These resources are around just to allow their values to be customized +     for different hardware and product builds. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + +    <!-- Whether action menu items should obey the "withText" showAsAction +         flag. This may be set to false for situations where space is +         extremely limited. --> +    <bool name="abs__config_allowActionMenuItemTextWithIcon">true</bool> + +</resources> diff --git a/libraries/ActionBarSherlock/res/values-w500dp/abs__dimens.xml b/libraries/ActionBarSherlock/res/values-w500dp/abs__dimens.xml new file mode 100644 index 000000000..2fd4deea2 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-w500dp/abs__dimens.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/dimens.xml +** +** Copyright 2006, 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="abs__max_action_buttons">4</integer> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-w600dp/abs__dimens.xml b/libraries/ActionBarSherlock/res/values-w600dp/abs__dimens.xml new file mode 100644 index 000000000..b085952d3 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-w600dp/abs__dimens.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/dimens.xml +** +** Copyright 2006, 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="abs__max_action_buttons">5</integer> +</resources> diff --git a/libraries/ActionBarSherlock/res/values-xlarge/abs__dimens.xml b/libraries/ActionBarSherlock/res/values-xlarge/abs__dimens.xml new file mode 100644 index 000000000..bfc535de1 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values-xlarge/abs__dimens.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/dimens.xml +** +** Copyright 2006, 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> +    <!-- Default height of an action bar. --> +    <dimen name="abs__action_bar_default_height">56dip</dimen> +    <!-- Vertical padding around action bar icons. --> +    <dimen name="abs__action_bar_icon_vertical_padding">4dip</dimen> +    <!-- Text size for action bar titles --> +    <dimen name="abs__action_bar_title_text_size">18dp</dimen> +    <!-- Text size for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_text_size">14dp</dimen> +    <!-- Top margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_top_margin">-3dp</dimen> +    <!-- Bottom margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_bottom_margin">9dip</dimen> + +    <!-- Minimum width for an action button in the menu area of an action bar --> +    <dimen name="abs__action_button_min_width">64dip</dimen> +     +    <!-- The platform's desired minimum size for a dialog's width when it +         is along the major axis (that is the screen is landscape).  This may +         be either a fraction or a dimension. --> +    <item type="dimen" name="abs__dialog_min_width_major">45%</item> +    <!-- The platform's desired minimum size for a dialog's width when it +         is along the minor axis (that is the screen is portrait).  This may +         be either a fraction or a dimension. --> +    <item type="dimen" name="abs__dialog_min_width_minor">72%</item> +</resources> diff --git a/libraries/ActionBarSherlock/res/values/abs__attrs.xml b/libraries/ActionBarSherlock/res/values/abs__attrs.xml new file mode 100644 index 000000000..32631ca8d --- /dev/null +++ b/libraries/ActionBarSherlock/res/values/abs__attrs.xml @@ -0,0 +1,432 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> +    <attr name="titleTextStyle" format="reference" /> +    <attr name="subtitleTextStyle" format="reference" /> +    <attr name="background" format="reference|color" /> +    <attr name="backgroundSplit" format="reference|color" /> +    <attr name="height" format="dimension" /> +    <attr name="divider" format="reference" /> + +    <declare-styleable name="SherlockTheme"> +        <!-- =================== --> +        <!-- Action bar styles   --> +        <!-- =================== --> +        <eat-comment /> +        <!-- Default style for tabs within an action bar --> +        <attr name="actionBarTabStyle" format="reference" /> +        <attr name="actionBarTabBarStyle" format="reference" /> +        <attr name="actionBarTabTextStyle" format="reference" /> +        <attr name="actionOverflowButtonStyle" format="reference" /> +        <!-- Reference to a style for the Action Bar --> +        <attr name="actionBarStyle" format="reference" /> +        <!-- Reference to a style for the split Action Bar. This style +             controls the split component that holds the menu/action +             buttons. actionBarStyle is still used for the primary +             bar. --> +        <attr name="actionBarSplitStyle" format="reference" /> +        <!-- Reference to a theme that should be used to inflate widgets +             and layouts destined for the action bar. Most of the time +             this will be a reference to the current theme, but when +             the action bar has a significantly different contrast +             profile than the rest of the activity the difference +             can become important. If this is set to @null the current +             theme will be used.--> +        <attr name="actionBarWidgetTheme" format="reference" /> +        <!-- Size of the Action Bar, including the contextual +             bar used to present Action Modes. --> +        <attr name="actionBarSize" format="dimension" > +            <enum name="wrap_content" value="0" /> +        </attr> +        <!-- Custom divider drawable to use for elements in the action bar. --> +        <attr name="actionBarDivider" format="reference" /> +        <!-- Custom item state list drawable background for action bar items. --> +        <attr name="actionBarItemBackground" format="reference" /> +        <!-- TextAppearance style that will be applied to text that +             appears within action menu items. --> +        <attr name="actionMenuTextAppearance" format="reference" /> +        <!-- Color for text that appears within action menu items. --> +        <attr name="actionMenuTextColor" format="color|reference" /> + +        <!-- =================== --> +        <!-- Action mode styles  --> +        <!-- =================== --> +        <eat-comment /> +        <attr name="actionModeStyle" format="reference" /> +        <attr name="actionModeCloseButtonStyle" format="reference" /> +        <!-- Background drawable to use for action mode UI --> +        <attr name="actionModeBackground" format="reference" /> +        <!-- Background drawable to use for action mode UI in the lower split bar --> +        <attr name="actionModeSplitBackground" format="reference" /> +        <!-- Drawable to use for the close action mode button --> +        <attr name="actionModeCloseDrawable" format="reference" /> +        <!-- Drawable to use for the Share action button in WebView selection action modes --> +        <attr name="actionModeShareDrawable" format="reference" /> + +        <!-- PopupWindow style to use for action modes when showing as a window overlay. --> +        <attr name="actionModePopupWindowStyle" format="reference" /> + +        <!-- ============= --> +        <!-- Button styles --> +        <!-- ============= --> +        <eat-comment /> + +        <!-- Small Button style. --> +        <attr name="buttonStyleSmall" format="reference" /> + +        <!-- Background drawable for standalone items that need focus/pressed states. --> +        <attr name="selectableItemBackground" format="reference" /> + + + +        <!-- This Drawable is overlaid over the foreground of the Window's content area, usually +             to place a shadow below the title.  --> +        <attr name="windowContentOverlay" format="reference" /> + +        <!-- Text color, typeface, size, and style for the text inside of a popup menu. --> +        <attr name="textAppearanceLargePopupMenu" format="reference" /> + +        <!-- Text color, typeface, size, and style for small text inside of a popup menu. --> +        <attr name="textAppearanceSmallPopupMenu" format="reference" /> + + +        <!-- Text color, typeface, size, and style for "small" text. Defaults to secondary text color. --> +        <attr name="textAppearanceSmall" format="reference" /> + +        <attr name="textColorPrimary" format="color" /> +        <attr name="textColorPrimaryDisableOnly" format="color" /> +        <attr name="textColorPrimaryInverse" format="color" /> + +        <attr name="spinnerItemStyle" format="reference" /> +        <attr name="spinnerDropDownItemStyle" format="reference" /> + +        <!-- ============================ --> +        <!-- SearchView styles and assets --> +        <!-- ============================ --> +        <eat-comment /> +        <!-- SearchView AutoCompleteTextView style --> +        <attr name="searchAutoCompleteTextView" format="reference" /> +        <!-- SearchView dropdown background --> +        <attr name="searchDropdownBackground" format="reference" /> +        <!-- SearchView close button icon --> +        <attr name="searchViewCloseIcon" format="reference" /> +        <!-- SearchView Go button icon --> +        <attr name="searchViewGoIcon" format="reference" /> +        <!-- SearchView Search icon --> +        <attr name="searchViewSearchIcon" format="reference" /> +        <!-- SearchView Voice button icon --> +        <attr name="searchViewVoiceIcon" format="reference" /> +        <!-- SearchView query refinement icon --> +        <attr name="searchViewEditQuery" format="reference" /> +        <!-- SearchView query refinement icon background --> +        <attr name="searchViewEditQueryBackground" format="reference" /> +        <!-- SearchView text field background for the left section --> +        <attr name="searchViewTextField" format="reference" /> +        <!-- SearchView text field background for the right section --> +        <attr name="searchViewTextFieldRight" format="reference" /> +        <!-- Text color for urls in search suggestions, used by things like global search and the browser. @hide --> +        <attr name="textColorSearchUrl" format="reference|color" /> +        <!-- The list item height for search results. @hide --> +        <attr name="searchResultListItemHeight" format="dimension" /> +        <!-- Text color, typeface, size, and style for system search result title. Defaults to primary inverse text color. --> +        <attr name="textAppearanceSearchResultTitle" format="reference" /> +        <!-- Text color, typeface, size, and style for system search result subtitle. Defaults to primary inverse text color. --> +        <attr name="textAppearanceSearchResultSubtitle" format="reference" /> + + +        <!-- =========== --> +        <!-- List styles --> +        <!-- =========== --> +        <eat-comment /> + +        <!-- A smaller, sleeker list item height. --> +        <attr name="listPreferredItemHeightSmall" format="dimension" /> + +        <!-- The preferred padding along the left edge of list items. --> +        <attr name="listPreferredItemPaddingLeft" format="dimension" /> +        <!-- The preferred padding along the right edge of list items. --> +        <attr name="listPreferredItemPaddingRight" format="dimension" /> + +        <!-- The preferred TextAppearance for the primary text of small list items. --> +        <attr name="textAppearanceListItemSmall" format="reference" /> + + +        <attr name="windowMinWidthMajor" format="dimension" /> +        <attr name="windowMinWidthMinor" format="dimension" /> + + + +        <!-- Drawable to use for generic vertical dividers. --> +        <attr name="dividerVertical" format="reference" /> + +        <attr name="actionDropDownStyle" format="reference" /> +        <attr name="actionButtonStyle" format="reference" /> +        <attr name="homeAsUpIndicator" format="reference" /> +        <attr name="dropDownListViewStyle" format="reference" /> +        <attr name="popupMenuStyle" format="reference" /> +        <attr name="dropdownListPreferredItemHeight" format="dimension" /> +        <attr name="actionSpinnerItemStyle" format="reference" /> +        <attr name="windowNoTitle" format="boolean"/> +        <attr name="windowActionBar" format="boolean"/> +        <attr name="windowActionBarOverlay" format="boolean"/> +        <attr name="windowActionModeOverlay" format="boolean"/> +        <attr name="windowSplitActionBar" format="boolean" /> + + +        <attr name="listPopupWindowStyle" format="reference" /> + + +        <!-- Default ActivityChooserView style. --> +        <attr name="activityChooserViewStyle" format="reference" /> +        <!-- Drawable used as a background for activated items. --> +        <attr name="activatedBackgroundIndicator" format="reference" /> + +        <attr name="android:windowIsFloating" /> +    </declare-styleable> + + +    <!-- Attributes used to style the Action Bar. --> +    <declare-styleable name="SherlockActionBar"> +        <!-- The type of navigation to use. --> +        <attr name="navigationMode"> +            <!-- Normal static title text --> +            <enum name="normal" value="0" /> +            <!-- The action bar will use a selection list for navigation. --> +            <enum name="listMode" value="1" /> +            <!-- The action bar will use a series of horizontal tabs for navigation. --> +            <enum name="tabMode" value="2" /> +        </attr> +        <!-- Options affecting how the action bar is displayed. --> +        <attr name="displayOptions"> +            <flag name="useLogo" value="0x1" /> +            <flag name="showHome" value="0x2" /> +            <flag name="homeAsUp" value="0x4" /> +            <flag name="showTitle" value="0x8" /> +            <flag name="showCustom" value="0x10" /> +            <flag name="disableHome" value="0x20" /> +        </attr> +        <!-- Specifies title text used for navigationMode="normal" --> +        <attr name="title" format="string" /> +        <!-- Specifies subtitle text used for navigationMode="normal" --> +        <attr name="subtitle" format="string" /> +        <!-- Specifies a style to use for title text. --> +        <attr name="titleTextStyle" /> +        <!-- Specifies a style to use for subtitle text. --> +        <attr name="subtitleTextStyle" /> +        <!-- Specifies the drawable used for the application icon. --> +        <attr name="icon" format="reference" /> +        <!-- Specifies the drawable used for the application logo. --> +        <attr name="logo" format="reference" /> +        <!-- Specifies the drawable used for item dividers. --> +        <attr name="divider" /> +        <!-- Specifies a background drawable for the action bar. --> +        <attr name="background" /> +        <!-- Specifies a background drawable for a second stacked row of the action bar. --> +        <attr name="backgroundStacked" format="reference|color" /> +        <!-- Specifies a background drawable for the bottom component of a split action bar. --> +        <attr name="backgroundSplit" /> +        <!-- Specifies a layout for custom navigation. Overrides navigationMode. --> +        <attr name="customNavigationLayout" format="reference" /> +        <!-- Specifies a fixed height. --> +        <attr name="height" /> +        <!-- Specifies a layout to use for the "home" section of the action bar. --> +        <attr name="homeLayout" format="reference" /> +        <!-- Specifies a style resource to use for an embedded progress bar. --> +        <attr name="progressBarStyle" format="reference" /> +        <!-- Specifies a style resource to use for an indeterminate progress spinner. --> +        <attr name="indeterminateProgressStyle" format="reference" /> +        <!-- Specifies the horizontal padding on either end for an embedded progress bar. --> +        <attr name="progressBarPadding" format="dimension" /> +        <!-- Specifies padding that should be applied to the left and right sides of +             system-provided items in the bar. --> +        <attr name="itemPadding" format="dimension" /> +    </declare-styleable> + + +    <declare-styleable name="SherlockActionMode"> +        <!-- Specifies a style to use for title text. --> +        <attr name="titleTextStyle" /> +        <!-- Specifies a style to use for subtitle text. --> +        <attr name="subtitleTextStyle" /> +        <!-- Specifies a background for the action mode bar. --> +        <attr name="background" /> +        <!-- Specifies a background for the split action mode bar. --> +        <attr name="backgroundSplit" /> +        <!-- Specifies a fixed height for the action mode bar. --> +        <attr name="height" /> +    </declare-styleable> + +    <declare-styleable name="SherlockMenuView"> +        <!-- Default appearance of menu item text. --> +        <attr name="itemTextAppearance" format="reference" /> +        <!-- Default horizontal divider between rows of menu items. --> +        <attr name="horizontalDivider" format="reference" /> +        <!-- Default vertical divider between menu items. --> +        <attr name="verticalDivider" format="reference" /> +        <!-- Default background for the menu header. --> +        <attr name="headerBackground" format="color|reference" /> +        <!-- Default background for each menu item. --> +        <attr name="itemBackground" format="color|reference" /> +        <!-- Default animations for the menu. --> +        <attr name="windowAnimationStyle" format="reference" /> +        <!-- Default disabled icon alpha for each menu item that shows an icon. --> +        <attr name="itemIconDisabledAlpha" format="float" /> +        <!-- Whether space should be reserved in layout when an icon is missing. --> +        <attr name="preserveIconSpacing" format="boolean" /> +    </declare-styleable> + +    <declare-styleable name="SherlockActionMenuItemView"> +        <attr name="android:minWidth" /> +    </declare-styleable> + +    <declare-styleable name="SherlockActivityChooserView"> +        <!-- The maximal number of items initially shown in the activity list. --> +        <attr name="initialActivityCount" format="string" /> +        <!-- The drawable to show in the button for expanding the activities overflow popup. +             <strong>Note:</strong> Clients would like to set this drawable +             as a clue about the action the chosen activity will perform. For +             example, if share activity is to be chosen the drawable should +             give a clue that sharing is to be performed. +         --> +        <attr name="expandActivityOverflowButtonDrawable" format="reference" /> + +        <attr name="android:background" /> +    </declare-styleable> + +    <!-- Base attributes that are available to all groups. --> +    <declare-styleable name="SherlockMenuGroup"> + +        <!-- The ID of the group. --> +        <attr name="android:id" /> + +        <!-- The category applied to all items within this group. +             (This will be or'ed with the orderInCategory attribute.) --> +        <attr name="android:menuCategory" /> + +        <!-- The order within the category applied to all items within this group. +             (This will be or'ed with the category attribute.) --> +        <attr name="android:orderInCategory" /> + +        <!-- Whether the items are capable of displaying a check mark. --> +        <attr name="android:checkableBehavior" /> + +        <!-- Whether the items are shown/visible. --> +        <attr name="android:visible" /> + +        <!-- Whether the items are enabled. --> +        <attr name="android:enabled" /> + +    </declare-styleable> + +    <!-- Base attributes that are available to all Item objects. --> +    <declare-styleable name="SherlockMenuItem"> + +        <!-- The ID of the item. --> +        <attr name="android:id" /> + +        <!-- The category applied to the item. +             (This will be or'ed with the orderInCategory attribute.) --> +        <attr name="android:menuCategory" /> + +        <!-- The order within the category applied to the item. +             (This will be or'ed with the category attribute.) --> +        <attr name="android:orderInCategory" /> + +        <!-- The title associated with the item. --> +        <attr name="android:title" /> + +        <!-- The condensed title associated with the item.  This is used in situations where the +             normal title may be too long to be displayed. --> +        <attr name="android:titleCondensed" /> + +        <!-- The icon associated with this item.  This icon will not always be shown, so +             the title should be sufficient in describing this item. --> +        <attr name="android:icon" /> + +        <!-- The alphabetic shortcut key.  This is the shortcut when using a keyboard +             with alphabetic keys. --> +        <attr name="android:alphabeticShortcut" /> + +        <!-- The numeric shortcut key.  This is the shortcut when using a numeric (e.g., 12-key) +             keyboard. --> +        <attr name="android:numericShortcut" /> + +        <!-- Whether the item is capable of displaying a check mark. --> +        <attr name="android:checkable" /> + +        <!-- Whether the item is checked.  Note that you must first have enabled checking with +             the checkable attribute or else the check mark will not appear. --> +        <attr name="android:checked" /> + +        <!-- Whether the item is shown/visible. --> +        <attr name="android:visible" /> + +        <!-- Whether the item is enabled. --> +        <attr name="android:enabled" /> + +        <!-- Name of a method on the Context used to inflate the menu that will be +             called when the item is clicked. --> +        <attr name="android:onClick" /> + +        <!-- How this item should display in the Action Bar, if present. --> +        <attr name="android:showAsAction" /> + +        <!-- An optional layout to be used as an action view. +             See {@link android.view.MenuItem#setActionView(android.view.View)} +             for more info. --> +        <attr name="android:actionLayout" /> + +        <!-- The name of an optional View class to instantiate and use as an +             action view. See {@link android.view.MenuItem#setActionView(android.view.View)} +             for more info. --> +        <attr name="android:actionViewClass" /> + +        <!-- The name of an optional ActionProvider class to instantiate an action view +             and perform operations such as default action for that menu item. +             See {@link android.view.MenuItem#setActionProvider(android.view.ActionProvider)} +             for more info. --> +        <attr name="android:actionProviderClass" /> + +    </declare-styleable> + +    <declare-styleable name="SherlockSpinner"> +        <!-- The prompt to display when the spinner's dialog is shown. --> +        <attr name="android:prompt" /> +        <!-- List selector to use for spinnerMode="dropdown" display. --> +        <attr name="android:dropDownSelector" /> +        <!-- Background drawable to use for the dropdown in spinnerMode="dropdown". --> +        <attr name="android:popupBackground" /> +        <!-- Vertical offset from the spinner widget for positioning the dropdown in +             spinnerMode="dropdown". --> +        <attr name="android:dropDownVerticalOffset" /> +        <!-- Horizontal offset from the spinner widget for positioning the dropdown +             in spinnerMode="dropdown". --> +        <attr name="android:dropDownHorizontalOffset" /> +        <!-- Width of the dropdown in spinnerMode="dropdown". --> +        <attr name="android:dropDownWidth" /> +        <!-- Reference to a layout to use for displaying a prompt in the dropdown for +             spinnerMode="dropdown". This layout must contain a TextView with the id +             @android:id/text1 to be populated with the prompt text. --> +        <attr name="android:popupPromptView" /> +        <!-- Gravity setting for positioning the currently selected item. --> +        <attr name="android:gravity" /> +    </declare-styleable> + +    <declare-styleable name="SherlockSearchView"> +        <!-- The default state of the SearchView. If true, it will be iconified when not in +             use and expanded when clicked. --> +        <attr name="iconifiedByDefault" format="boolean"/> +        <!-- An optional maximum width of the SearchView. --> +        <attr name="android:maxWidth" /> +        <!-- An optional query hint string to be displayed in the empty query field. --> +        <attr name="queryHint" format="string" /> +        <!-- The IME options to set on the query text field. --> +        <attr name="android:imeOptions" /> +        <!-- The input type to set on the query text field. --> +        <attr name="android:inputType" /> +    </declare-styleable> + +    <declare-styleable name="SherlockView"> +        <attr name="android:focusable"/> +    </declare-styleable> +</resources> diff --git a/libraries/ActionBarSherlock/res/values/abs__bools.xml b/libraries/ActionBarSherlock/res/values/abs__bools.xml new file mode 100644 index 000000000..0b432448d --- /dev/null +++ b/libraries/ActionBarSherlock/res/values/abs__bools.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<resources> +    <bool name="abs__action_bar_embed_tabs">false</bool> +    <bool name="abs__split_action_bar_is_narrow">true</bool> +    <bool name="abs__action_bar_expanded_action_views_exclusive">true</bool> +    <!--bool name="target_honeycomb_needs_options_menu">true</bool--> +</resources> diff --git a/libraries/ActionBarSherlock/res/values/abs__colors.xml b/libraries/ActionBarSherlock/res/values/abs__colors.xml new file mode 100644 index 000000000..625c632ff --- /dev/null +++ b/libraries/ActionBarSherlock/res/values/abs__colors.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<resources> +    <color name="abs__background_holo_dark">#ff000000</color> +    <color name="abs__background_holo_light">#fff3f3f3</color> +    <color name="abs__bright_foreground_holo_dark">@color/abs__background_holo_light</color> +    <color name="abs__bright_foreground_holo_light">@color/abs__background_holo_dark</color> +    <color name="abs__bright_foreground_disabled_holo_dark">#ff4c4c4c</color> +    <color name="abs__bright_foreground_disabled_holo_light">#ffb2b2b2</color> +    <color name="abs__bright_foreground_inverse_holo_dark">@color/abs__bright_foreground_holo_light</color> +    <color name="abs__bright_foreground_inverse_holo_light">@color/abs__bright_foreground_holo_dark</color> +    <color name="abs__holo_blue_light">#ff33b5e5</color> +</resources> diff --git a/libraries/ActionBarSherlock/res/values/abs__config.xml b/libraries/ActionBarSherlock/res/values/abs__config.xml new file mode 100644 index 000000000..4c7b5d459 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values/abs__config.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2009, 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. +*/ +--> + +<!-- These resources are around just to allow their values to be customized +     for different hardware and product builds. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + +    <!-- The maximum width we would prefer dialogs to be.  0 if there is no +         maximum (let them grow as large as the screen).  Actual values are +         specified for -large and -xlarge configurations. --> +    <dimen name="abs__config_prefDialogWidth">320dp</dimen> + +    <!-- Sets whether menu shortcuts should be displayed on panel menus when +         a keyboard is present. --> +    <bool name="abs__config_showMenuShortcutsWhenKeyboardPresent">false</bool> + +    <!-- Whether action menu items should be displayed in ALLCAPS or not. +         Defaults to true. If this is not appropriate for specific locales +         it should be disabled in that locale's resources. --> +    <bool name="abs__config_actionMenuItemAllCaps">true</bool> + +    <!-- Whether action menu items should obey the "withText" showAsAction +         flag. This may be set to false for situations where space is +         extremely limited. --> +    <bool name="abs__config_allowActionMenuItemTextWithIcon">false</bool> + +</resources> diff --git a/libraries/ActionBarSherlock/res/values/abs__dimens.xml b/libraries/ActionBarSherlock/res/values/abs__dimens.xml new file mode 100644 index 000000000..831289e07 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values/abs__dimens.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/dimens.xml +** +** Copyright 2006, 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> +    <!-- Default height of an action bar. --> +    <dimen name="abs__action_bar_default_height">48dip</dimen> +    <!-- Vertical padding around action bar icons. --> +    <dimen name="abs__action_bar_icon_vertical_padding">8dip</dimen> +    <!-- Text size for action bar titles --> +    <dimen name="abs__action_bar_title_text_size">18dp</dimen> +    <!-- Text size for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_text_size">14dp</dimen> +    <!-- Top margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_top_margin">-3dp</dimen> +    <!-- Bottom margin for action bar subtitles --> +    <dimen name="abs__action_bar_subtitle_bottom_margin">5dip</dimen> + +    <integer name="abs__max_action_buttons">2</integer> + +    <!-- Minimum width for an action button in the menu area of an action bar --> +    <dimen name="abs__action_button_min_width">56dip</dimen> + +    <!-- Dialog title height --> +    <dimen name="abs__alert_dialog_title_height">64dip</dimen> + +    <!-- The platform's desired minimum size for a dialog's width when it +         is along the major axis (that is the screen is landscape).  This may +         be either a fraction or a dimension. --> +    <item type="dimen" name="abs__dialog_min_width_major">65%</item> +    <!-- The platform's desired minimum size for a dialog's width when it +         is along the minor axis (that is the screen is portrait).  This may +         be either a fraction or a dimension. --> +    <item type="dimen" name="abs__dialog_min_width_minor">95%</item> + + +    <!-- Text padding for dropdown items --> +    <dimen name="abs__dropdownitem_text_padding_left">8dip</dimen> + +    <!-- Text padding for dropdown items --> +    <dimen name="abs__dropdownitem_text_padding_right">8dip</dimen> + +    <!-- Width of the icon in a dropdown list --> +    <dimen name="abs__dropdownitem_icon_width">32dip</dimen> + + +    <!-- Minimum width of the search view text entry area. --> +    <dimen name="abs__search_view_text_min_width">160dip</dimen> + +    <!-- Preferred width of the search view. --> +    <dimen name="abs__search_view_preferred_width">320dip</dimen> +</resources> diff --git a/libraries/ActionBarSherlock/res/values/abs__ids.xml b/libraries/ActionBarSherlock/res/values/abs__ids.xml new file mode 100644 index 000000000..f9f56045b --- /dev/null +++ b/libraries/ActionBarSherlock/res/values/abs__ids.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2007, 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> +  <item type="id" name="abs__home" /> +  <item type="id" name="abs__up" /> +  <item type="id" name="abs__action_menu_divider" /> +  <item type="id" name="abs__action_menu_presenter" /> +  <item type="id" name="abs__progress_circular" /> +  <item type="id" name="abs__progress_horizontal" /> +</resources> diff --git a/libraries/ActionBarSherlock/res/values/abs__strings.xml b/libraries/ActionBarSherlock/res/values/abs__strings.xml new file mode 100644 index 000000000..06a2a2af4 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values/abs__strings.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2007, 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"> +    <!-- Content description for the action bar "home" affordance. [CHAR LIMIT=NONE] --> +    <string name="abs__action_bar_home_description">Navigate home</string> +    <!-- Content description for the action bar "up" affordance. [CHAR LIMIT=NONE] --> +    <string name="abs__action_bar_up_description">Navigate up</string> +    <!-- Content description for the action menu overflow button. [CHAR LIMIT=NONE] --> +    <string name="abs__action_menu_overflow_description">More options</string> + +    <!-- Label for the "Done" button on the far left of action mode toolbars. --> +    <string name="abs__action_mode_done">Done</string> + +    <!-- Title for a button to expand the list of activities in ActivityChooserView [CHAR LIMIT=25] --> +    <string name="abs__activity_chooser_view_see_all">See all...</string> +    <!-- Title default for a dialog showing possible activities in ActivityChooserView [CHAR LIMIT=25] --> +    <string name="abs__activity_chooser_view_dialog_title_default">Select activity</string> +    <!-- Title for a dialog showing possible activities for sharing in ShareActionProvider [CHAR LIMIT=25] --> +    <string name="abs__share_action_provider_share_with">Share with...</string> +    <!-- Description of the shwoing of a popup window with activities to choose from. [CHAR LIMIT=NONE] --> +    <string name="abs__activitychooserview_choose_application">Choose an application</string> +    <!-- Description of the choose target button in a ShareActionProvider (share UI). [CHAR LIMIT=NONE] --> +    <string name="abs__shareactionprovider_share_with">Share with</string> +    <!-- Description of a share target (both in the list of such or the default share button) in a ShareActionProvider (share UI). [CHAR LIMIT=NONE] --> +    <string name="abs__shareactionprovider_share_with_application">Share with <xliff:g id="application_name" example="Bluetooth">%s</xliff:g></string> + +    <!-- SearchView accessibility description for search button [CHAR LIMIT=NONE] --> +    <string name="abs__searchview_description_search">Search</string> +    <!-- SearchView accessibility description for search text field [CHAR LIMIT=NONE] --> +    <string name="abs__searchview_description_query">Search query</string> +    <!-- SearchView accessibility description for clear button [CHAR LIMIT=NONE] --> +    <string name="abs__searchview_description_clear">Clear query</string> +    <!-- SearchView accessibility description for submit button [CHAR LIMIT=NONE] --> +    <string name="abs__searchview_description_submit">Submit query</string> +    <!-- SearchView accessibility description for voice button [CHAR LIMIT=NONE] --> +    <string name="abs__searchview_description_voice">Voice search</string> +</resources> diff --git a/libraries/ActionBarSherlock/res/values/abs__styles.xml b/libraries/ActionBarSherlock/res/values/abs__styles.xml new file mode 100644 index 000000000..45a18c183 --- /dev/null +++ b/libraries/ActionBarSherlock/res/values/abs__styles.xml @@ -0,0 +1,412 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> +    <style name="Widget"> +    </style> + +    <style name="Sherlock.__Widget.ActionBar" parent="Widget"> +        <item name="displayOptions">useLogo|showHome|showTitle</item> +        <item name="height">?attr/actionBarSize</item> +        <item name="android:paddingLeft">0dip</item> +        <item name="android:paddingTop">0dip</item> +        <item name="android:paddingRight">0dip</item> +        <item name="android:paddingBottom">0dip</item> +        <item name="homeLayout">@layout/abs__action_bar_home</item> +    </style> +    <style name="Widget.Sherlock.ActionBar" parent="Sherlock.__Widget.ActionBar"> +        <item name="titleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionBar.Title</item> +        <item name="subtitleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionBar.Subtitle</item> +        <item name="background">@drawable/abs__ab_transparent_dark_holo</item> +        <item name="backgroundStacked">@drawable/abs__ab_stacked_transparent_dark_holo</item> +        <item name="backgroundSplit">@drawable/abs__ab_bottom_transparent_dark_holo</item> +        <item name="divider">?attr/dividerVertical</item> +        <item name="progressBarStyle">@style/Widget.Sherlock.ProgressBar.Horizontal</item> +        <item name="indeterminateProgressStyle">@style/Widget.Sherlock.ProgressBar</item> +        <item name="progressBarPadding">32dip</item> +        <item name="itemPadding">8dip</item> +    </style> +    <style name="Widget.Sherlock.ActionBar.Solid" parent="Sherlock.__Widget.ActionBar"> +        <item name="titleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionBar.Title</item> +        <item name="subtitleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionBar.Subtitle</item> +        <item name="background">@drawable/abs__ab_solid_dark_holo</item> +        <item name="backgroundStacked">@drawable/abs__ab_stacked_solid_dark_holo</item> +        <item name="backgroundSplit">@drawable/abs__ab_bottom_solid_dark_holo</item> +        <item name="divider">?attr/dividerVertical</item> +        <item name="progressBarStyle">@style/Widget.Sherlock.ProgressBar.Horizontal</item> +        <item name="indeterminateProgressStyle">@style/Widget.Sherlock.ProgressBar</item> +        <item name="progressBarPadding">32dip</item> +        <item name="itemPadding">8dip</item> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar" parent="Widget.Sherlock.ActionBar"> +        <item name="titleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionBar.Title</item> +        <item name="subtitleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionBar.Subtitle</item> +        <item name="background">@drawable/abs__ab_transparent_light_holo</item> +        <item name="backgroundStacked">@drawable/abs__ab_stacked_transparent_light_holo</item> +        <item name="backgroundSplit">@drawable/abs__ab_bottom_transparent_light_holo</item> +        <item name="homeAsUpIndicator">@drawable/abs__ic_ab_back_holo_light</item> +        <item name="progressBarStyle">@style/Widget.Sherlock.Light.ProgressBar.Horizontal</item> +        <item name="indeterminateProgressStyle">@style/Widget.Sherlock.Light.ProgressBar</item> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.Solid"> +        <item name="titleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionBar.Title</item> +        <item name="subtitleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionBar.Subtitle</item> +        <item name="background">@drawable/abs__ab_solid_light_holo</item> +        <item name="backgroundStacked">@drawable/abs__ab_stacked_solid_light_holo</item> +        <item name="backgroundSplit">@drawable/abs__ab_bottom_solid_light_holo</item> +        <item name="divider">?attr/dividerVertical</item> +        <item name="progressBarStyle">@style/Widget.Sherlock.Light.ProgressBar.Horizontal</item> +        <item name="indeterminateProgressStyle">@style/Widget.Sherlock.Light.ProgressBar</item> +        <item name="progressBarPadding">32dip</item> +        <item name="itemPadding">8dip</item> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.Solid.Inverse"> +        <item name="titleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionBar.Title.Inverse</item> +        <item name="subtitleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionBar.Subtitle.Inverse</item> +        <item name="background">@drawable/abs__ab_solid_dark_holo</item> +        <item name="backgroundStacked">@drawable/abs__ab_stacked_solid_dark_holo</item> +        <item name="backgroundSplit">@drawable/abs__ab_bottom_solid_inverse_holo</item> +        <item name="divider">@drawable/abs__list_divider_holo_dark</item> +        <item name="progressBarStyle">@style/Widget.Sherlock.ProgressBar.Horizontal</item> +        <item name="indeterminateProgressStyle">@style/Widget.Sherlock.ProgressBar</item> +        <item name="progressBarPadding">32dip</item> +        <item name="itemPadding">8dip</item> +    </style> + +    <style name="Widget.Sherlock.ActionBar.TabView" parent="Widget"> +        <item name="android:gravity">center_horizontal</item> +        <item name="android:background">@drawable/abs__tab_indicator_ab_holo</item> +        <item name="android:paddingLeft">16dip</item> +        <item name="android:paddingRight">16dip</item> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.TabView" parent="Widget.Sherlock.ActionBar.TabView"> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.TabView.Inverse"> +    </style> + +    <style name="Widget.Sherlock.ActionBar.TabBar" parent="Widget"> +        <item name="android:divider">?attr/actionBarDivider</item> +        <item name="android:showDividers">middle</item> +        <item name="android:dividerPadding">12dip</item> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.TabBar" parent="Widget.Sherlock.ActionBar.TabBar"> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.TabBar.Inverse"> +    </style> + +    <style name="Widget.Sherlock.ActionBar.TabText" parent="Widget"> +        <item name="android:textAppearance">@null</item> +        <item name="android:textColor">?attr/textColorPrimary</item> +        <item name="android:textSize">12sp</item> +        <item name="android:textStyle">bold</item> +        <item name="android:textAllCaps">true</item> +        <item name="android:ellipsize">marquee</item> +        <item name="android:maxLines">2</item> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.TabText" parent="Widget.Sherlock.ActionBar.TabText"> +    </style> +    <style name="Widget.Sherlock.Light.ActionBar.TabText.Inverse"> +        <item name="android:textColor">?attr/textColorPrimaryInverse</item> +    </style> + +    <style name="Widget.Sherlock.ActionButton" parent="Widget"> +        <item name="android:background">?attr/actionBarItemBackground</item> +        <item name="android:minHeight">?attr/actionBarSize</item> + +        <item name="android:minWidth">@dimen/abs__action_button_min_width</item> +        <item name="android:gravity">center</item> +        <item name="android:paddingLeft">12dip</item> +        <item name="android:paddingRight">12dip</item> +        <item name="android:scaleType">center</item> +    </style> +    <style name="Widget.Sherlock.Light.ActionButton" parent="Widget.Sherlock.ActionButton"> +    </style> + +    <style name="Widget.Sherlock.ActionButton.CloseMode"> +        <item name="android:background">@drawable/abs__btn_cab_done_holo_dark</item> +    </style> +    <style name="Widget.Sherlock.Light.ActionButton.CloseMode"> +        <item name="android:background">@drawable/abs__btn_cab_done_holo_light</item> +    </style> + +    <style name="Widget.Sherlock.ActionButton.Overflow"> +        <item name="android:src">@drawable/abs__ic_menu_moreoverflow_holo_dark</item> +        <item name="android:background">?attr/actionBarItemBackground</item> +        <item name="android:contentDescription">@string/abs__action_menu_overflow_description</item> +    </style> +    <style name="Widget.Sherlock.Light.ActionButton.Overflow"> +        <item name="android:src">@drawable/abs__ic_menu_moreoverflow_holo_light</item> +    </style> + +    <style name="Sherlock.__Widget.ActionMode" parent="Widget"> +        <item name="background">?attr/actionModeBackground</item> +        <item name="backgroundSplit">?attr/actionModeSplitBackground</item> +        <item name="height">?attr/actionBarSize</item> +    </style> +    <style name="Widget.Sherlock.ActionMode" parent="Sherlock.__Widget.ActionMode"> +        <item name="titleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionMode.Title</item> +        <item name="subtitleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionMode.Subtitle</item> +    </style> +    <style name="Widget.Sherlock.Light.ActionMode" parent="Widget.Sherlock.ActionMode"> +        <item name="titleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionMode.Title</item> +        <item name="subtitleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionMode.Subtitle</item> +    </style> +    <style name="Widget.Sherlock.Light.ActionMode.Inverse" parent="Sherlock.__Widget.ActionMode"> +        <item name="titleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionMode.Title.Inverse</item> +        <item name="subtitleTextStyle">@style/TextAppearance.Sherlock.Widget.ActionMode.Subtitle.Inverse</item> +    </style> + + +    <style name="Widget.Sherlock.ListPopupWindow" parent="Widget"> +        <item name="android:dropDownSelector">@drawable/abs__list_selector_holo_dark</item> +        <item name="android:popupBackground">@drawable/abs__menu_dropdown_panel_holo_dark</item> +        <item name="android:dropDownVerticalOffset">0dip</item> +        <item name="android:dropDownHorizontalOffset">0dip</item> +        <item name="android:dropDownWidth">wrap_content</item> +    </style> +    <style name="Widget.Sherlock.Light.ListPopupWindow" parent="Widget"> +        <item name="android:dropDownSelector">@drawable/abs__list_selector_holo_light</item> +        <item name="android:popupBackground">@drawable/abs__menu_dropdown_panel_holo_light</item> +        <item name="android:dropDownVerticalOffset">0dip</item> +        <item name="android:dropDownHorizontalOffset">0dip</item> +        <item name="android:dropDownWidth">wrap_content</item> +    </style> +    <style name="Widget.Sherlock.PopupMenu" parent="Widget.Sherlock.ListPopupWindow"> +    </style> +    <style name="Widget.Sherlock.Light.PopupMenu" parent="Widget.Sherlock.Light.ListPopupWindow"> +    </style> + + +    <style name="Sherlock.__Widget.ActivityChooserView" parent="Widget"> +        <item name="android:gravity">center</item> +        <item name="android:background">@drawable/abs__ab_share_pack_holo_dark</item> +        <item name="android:divider">?attr/dividerVertical</item> +        <item name="android:showDividers">middle</item> +        <item name="android:dividerPadding">6dip</item> +        <item name="android:minHeight">?attr/actionBarSize</item> +    </style> +    <style name="Widget.Sherlock.ActivityChooserView" parent="Sherlock.__Widget.ActivityChooserView"> +    </style> +    <style name="Widget.Sherlock.Light.ActivityChooserView" parent="Widget.Sherlock.ActivityChooserView"> +        <item name="android:background">@drawable/abs__ab_share_pack_holo_light</item> +    </style> + +    <style name="Widget.Sherlock.Button.Small" parent="Widget"> +      <item name="android:textAppearance">?attr/textAppearanceSmall</item> +      <item name="android:textColor">@color/abs__primary_text_holo_dark</item> +      <item name="android:minHeight">48dip</item> +      <item name="android:minWidth">48dip</item> +    </style> +    <style name="Widget.Sherlock.Light.Button.Small" parent="Widget"> +      <item name="android:textAppearance">?attr/textAppearanceSmall</item> +      <item name="android:textColor">@color/abs__primary_text_holo_light</item> +      <item name="android:minHeight">48dip</item> +      <item name="android:minWidth">48dip</item> +    </style> + + +    <style name="Sherlock.__Widget.Holo.Spinner" parent="Widget"> +        <item name="android:dropDownSelector">@drawable/abs__list_selector_holo_dark</item> +        <item name="android:popupBackground">@drawable/abs__menu_dropdown_panel_holo_dark</item> +        <item name="android:dropDownVerticalOffset">0dip</item> +        <item name="android:dropDownHorizontalOffset">0dip</item> +        <item name="android:dropDownWidth">wrap_content</item> +        <item name="android:gravity">left|center_vertical</item> +        <item name="android:spinnerMode">dropdown</item> +        <item name="android:clickable">true</item> +    </style> +    <style name="Widget.Sherlock.Spinner.DropDown.ActionBar" parent="Sherlock.__Widget.Holo.Spinner"> +        <item name="android:background">@drawable/abs__spinner_ab_holo_dark</item> +    </style> +    <style name="Widget.Sherlock.Light.Spinner.DropDown.ActionBar" parent="Sherlock.__Widget.Holo.Spinner"> +        <item name="android:background">@drawable/abs__spinner_ab_holo_light</item> +        <item name="android:dropDownSelector">@drawable/abs__list_selector_holo_light</item> +        <item name="android:popupBackground">@drawable/abs__menu_dropdown_panel_holo_light</item> +    </style> + +    <style name="Sherlock.__Widget.Holo.ListView" parent="android:Widget.ListView"> +        <item name="android:divider">@drawable/abs__list_divider_holo_dark</item> +        <item name="android:listSelector">@drawable/abs__list_selector_holo_dark</item> +    </style> +    <style name="Widget.Sherlock.ListView.DropDown" parent="Sherlock.__Widget.Holo.ListView"> +    </style> +    <style name="Widget.Sherlock.Light.ListView.DropDown" parent="Sherlock.__Widget.Holo.ListView"> +        <item name="android:divider">@drawable/abs__list_divider_holo_light</item> +        <item name="android:listSelector">@drawable/abs__list_selector_holo_light</item> +    </style> + +    <style name="Sherlock.__Widget.Holo.DropDownItem" parent="Widget"> +        <item name="android:textAppearance">@style/TextAppearance.Sherlock.Widget.DropDownItem</item> +        <item name="android:paddingLeft">8dp</item> +        <item name="android:paddingRight">8dp</item> +        <item name="android:gravity">center_vertical</item> +    </style> +    <style name="Widget.Sherlock.DropDownItem.Spinner" parent="Sherlock.__Widget.Holo.DropDownItem"> +    </style> +    <style name="Widget.Sherlock.Light.DropDownItem.Spinner" parent="Sherlock.__Widget.Holo.DropDownItem"> +    </style> + +    <style name="Widget.Sherlock.PopupWindow.ActionMode" parent="Widget"> +    </style> +    <style name="Widget.Sherlock.Light.PopupWindow.ActionMode" parent="Widget"> +        <item name="android:popupBackground">@android:color/white</item> +    </style> + + + +    <style name="Widget.Sherlock.ProgressBar" parent="android:Widget.ProgressBar"> +        <item name="android:indeterminateDrawable">@drawable/abs__progress_medium_holo</item> +        <item name="android:animationResolution">33</item> +    </style> +    <style name="Widget.Sherlock.Light.ProgressBar" parent="Widget.Sherlock.ProgressBar"> +    </style> + +    <style name="Widget.Sherlock.ProgressBar.Horizontal" parent="android:Widget.ProgressBar.Horizontal"> +        <item name="android:progressDrawable">@drawable/abs__progress_horizontal_holo_dark</item> +        <!--item name="android:indeterminateDrawable">@drawable/abs__progress_indeterminate_horizontal_holo</item--> +        <item name="android:minHeight">16dip</item> +        <item name="android:maxHeight">16dip</item> +    </style> +    <style name="Widget.Sherlock.Light.ProgressBar.Horizontal" parent="Widget.Sherlock.ProgressBar.Horizontal"> +        <item name="android:progressDrawable">@drawable/abs__progress_horizontal_holo_light</item> +    </style> + + + +    <style name="Widget.Sherlock.TextView.SpinnerItem" parent="Widget"> +        <item name="android:textAppearance">@style/TextAppearance.Sherlock.Widget.TextView.SpinnerItem</item> +        <item name="android:paddingLeft">8dp</item> +        <item name="android:paddingRight">8dp</item> +    </style> + + + +    <style name="Sherlock.__Widget.SearchAutoCompleteTextView" parent="Widget"> +        <item name="android:focusable">true</item> +        <item name="android:focusableInTouchMode">true</item> +        <item name="android:clickable">true</item> +        <item name="android:textAppearance">?android:attr/textAppearanceMediumInverse</item> +        <item name="android:textColor">?attr/textColorPrimary</item> +        <item name="android:gravity">center_vertical</item> +        <item name="android:completionHintView">@layout/abs__simple_dropdown_hint</item> +        <item name="android:completionThreshold">2</item> +        <item name="android:dropDownWidth">wrap_content</item> +    </style> +    <style name="Widget.Sherlock.SearchAutoCompleteTextView" parent="Sherlock.__Widget.SearchAutoCompleteTextView"> +        <item name="android:dropDownSelector">@drawable/abs__list_selector_holo_dark</item> +        <item name="android:popupBackground">@drawable/abs__menu_dropdown_panel_holo_dark</item> +    </style> +    <style name="Widget.Sherlock.Light.SearchAutoCompleteTextView" parent="Sherlock.__Widget.SearchAutoCompleteTextView"> +        <item name="android:dropDownSelector">@drawable/abs__list_selector_holo_light</item> +        <item name="android:popupBackground">@drawable/abs__menu_dropdown_panel_holo_light</item> +    </style> + + + +    <style name="DialogWindowTitle.Sherlock" parent="Widget"> +        <item name="android:maxLines">1</item> +        <item name="android:scrollHorizontally">true</item> +        <item name="android:textAppearance">@style/TextAppearance.Sherlock.DialogWindowTitle</item> +        <item name="android:minHeight">@dimen/abs__alert_dialog_title_height</item> +        <item name="android:paddingLeft">16dip</item> +        <item name="android:paddingRight">16dip</item> +    </style> +    <style name="DialogWindowTitle.Sherlock.Light" parent="Widget"> +        <item name="android:maxLines">1</item> +        <item name="android:scrollHorizontally">true</item> +        <item name="android:textAppearance">@style/TextAppearance.Sherlock.Light.DialogWindowTitle</item> +        <item name="android:minHeight">@dimen/abs__alert_dialog_title_height</item> +        <item name="android:paddingLeft">16dip</item> +        <item name="android:paddingRight">16dip</item> +    </style> + + + +    <style name="TextAppearance.Sherlock.Widget.ActionBar.Menu" parent="Widget"> +        <item name="android:textSize">12sp</item> +        <item name="android:textStyle">bold</item> +        <item name="android:textColor">?attr/actionMenuTextColor</item> +        <item name="android:textAllCaps">@bool/abs__config_actionMenuItemAllCaps</item> +    </style> + +    <style name="TextAppearance.Sherlock.Widget.ActionBar.Title" parent="Widget"> +        <item name="android:textSize">@dimen/abs__action_bar_title_text_size</item> +        <item name="android:textColor">?android:attr/textColorPrimary</item> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionBar.Title.Inverse" parent="Widget"> +        <item name="android:textSize">@dimen/abs__action_bar_title_text_size</item> +        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionBar.Subtitle" parent="Widget"> +        <item name="android:textSize">@dimen/abs__action_bar_subtitle_text_size</item> +        <item name="android:textColor">?android:attr/textColorSecondary</item> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionBar.Subtitle.Inverse" parent="Widget"> +        <item name="android:textSize">@dimen/abs__action_bar_subtitle_text_size</item> +        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionMode.Title" parent="Widget"> +        <item name="android:textSize">@dimen/abs__action_bar_title_text_size</item> +        <item name="android:textColor">?android:attr/textColorPrimary</item> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionMode.Title.Inverse" parent="Widget"> +        <item name="android:textSize">@dimen/abs__action_bar_title_text_size</item> +        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionMode.Subtitle" parent="Widget"> +        <item name="android:textSize">@dimen/abs__action_bar_subtitle_text_size</item> +        <item name="android:textColor">?android:attr/textColorSecondary</item> +    </style> +    <style name="TextAppearance.Sherlock.Widget.ActionMode.Subtitle.Inverse" parent="Widget"> +        <item name="android:textSize">@dimen/abs__action_bar_subtitle_text_size</item> +        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> +    </style> + +    <style name="TextAppearance.Sherlock.Widget.PopupMenu" parent="Widget"> +        <item name="android:textColor">?attr/textColorPrimary</item> +    </style> +    <style name="TextAppearance.Sherlock.Widget.PopupMenu.Large"> +        <item name="android:textSize">18sp</item> +    </style> +    <style name="TextAppearance.Sherlock.Light.Widget.PopupMenu.Large" parent="TextAppearance.Sherlock.Widget.PopupMenu.Large"> +    </style> +    <style name="TextAppearance.Sherlock.Widget.PopupMenu.Small"> +        <item name="android:textSize">14sp</item> +    </style> +    <style name="TextAppearance.Sherlock.Light.Widget.PopupMenu.Small" parent="TextAppearance.Sherlock.Widget.PopupMenu.Small"> +    </style> + +    <style name="TextAppearance.Sherlock.Widget.TextView.SpinnerItem" parent="Widget"> +        <item name="android:textColor">?textColorPrimary</item> +        <item name="android:textSize">16sp</item> +        <item name="android:textStyle">normal</item> +    </style> + +    <style name="TextAppearance.Sherlock.Widget.DropDownItem" parent="Widget"> +        <item name="android:textColor">?textColorPrimaryDisableOnly</item> +        <item name="android:textSize">16sp</item> +        <item name="android:textStyle">normal</item> +    </style> + +    <style name="TextAppearance.Sherlock.DialogWindowTitle" parent="Widget"> +        <item name="android:textSize">22sp</item> +        <item name="android:textColor">@color/abs__holo_blue_light</item> +    </style> +    <style name="TextAppearance.Sherlock.Light.DialogWindowTitle" parent="Widget"> +        <item name="android:textSize">22sp</item> +        <item name="android:textColor">@color/abs__holo_blue_light</item> +    </style> + +    <style name="Sherlock.__TextAppearance.Small" parent="Widget"> +      <item name="android:textSize">14sp</item> +      <item name="android:textColor">?android:attr/textColorSecondary</item> +    </style> +    <style name="TextAppearance.Sherlock.Small" parent="Sherlock.__TextAppearance.Small"> +    </style> +    <style name="TextAppearance.Sherlock.Light.Small" parent="TextAppearance.Sherlock.Small"> +    </style> + +    <style name="TextAppearance.Sherlock.Widget.DropDownHint" parent="Widget"> +        <item name="android:textColor">?textColorPrimary</item> +        <item name="android:textSize">14sp</item> +    </style> +</resources> diff --git a/libraries/ActionBarSherlock/res/values/abs__themes.xml b/libraries/ActionBarSherlock/res/values/abs__themes.xml new file mode 100644 index 000000000..634fa798b --- /dev/null +++ b/libraries/ActionBarSherlock/res/values/abs__themes.xml @@ -0,0 +1,239 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> +    <style name="Sherlock.__Theme" parent="android:Theme.NoTitleBar"> +        <item name="android:windowContentOverlay">@null</item> +    </style> +    <style name="Sherlock.__Theme.Light" parent="android:Theme.Light.NoTitleBar"> +        <item name="android:windowContentOverlay">@null</item> +    </style> +    <style name="Sherlock.__Theme.DarkActionBar" parent="Theme.Sherlock.Light"> +    </style> +    <style name="Sherlock.__Theme.Dialog" parent="android:Theme.Dialog"> +    </style> + +    <style name="Theme.Sherlock" parent="Sherlock.__Theme"> +        <!-- Action bar styles (from Theme.Holo) --> +        <item name="actionDropDownStyle">@style/Widget.Sherlock.Spinner.DropDown.ActionBar</item> +        <item name="actionButtonStyle">@style/Widget.Sherlock.ActionButton</item> +        <item name="actionOverflowButtonStyle">@style/Widget.Sherlock.ActionButton.Overflow</item> +        <item name="actionModeBackground">@drawable/abs__cab_background_top_holo_dark</item> +        <item name="actionModeSplitBackground">@drawable/abs__cab_background_bottom_holo_dark</item> +        <item name="actionModeCloseDrawable">@drawable/abs__ic_cab_done_holo_dark</item> +        <item name="actionBarTabStyle">@style/Widget.Sherlock.ActionBar.TabView</item> +        <item name="actionBarTabBarStyle">@style/Widget.Sherlock.ActionBar.TabBar</item> +        <item name="actionBarTabTextStyle">@style/Widget.Sherlock.ActionBar.TabText</item> +        <item name="actionModeStyle">@style/Widget.Sherlock.ActionMode</item> +        <item name="actionModeCloseButtonStyle">@style/Widget.Sherlock.ActionButton.CloseMode</item> +        <item name="actionBarStyle">@style/Widget.Sherlock.ActionBar</item> +        <item name="actionBarSize">@dimen/abs__action_bar_default_height</item> +        <!-- Internal --><item name="actionModePopupWindowStyle">@style/Widget.Sherlock.PopupWindow.ActionMode</item> +        <item name="actionBarWidgetTheme">@null</item> + +        <!-- Action bar styles (defaults from Theme) --> +        <item name="actionBarSplitStyle">?attr/actionBarStyle</item> +        <item name="actionMenuTextAppearance">@style/TextAppearance.Sherlock.Widget.ActionBar.Menu</item> +        <item name="actionMenuTextColor">?attr/textColorPrimary</item> +        <item name="actionBarDivider">?attr/dividerVertical</item> +        <item name="actionBarItemBackground">?attr/selectableItemBackground</item> + +        <item name="buttonStyleSmall">@style/Widget.Sherlock.Button.Small</item> + +        <item name="selectableItemBackground">@drawable/abs__item_background_holo_dark</item> + +        <item name="activatedBackgroundIndicator">@drawable/abs__activated_background_holo_dark</item> +        <item name="actionModeShareDrawable">@drawable/abs__ic_menu_share_holo_dark</item> +        <item name="activityChooserViewStyle">@style/Widget.Sherlock.ActivityChooserView</item> + +        <item name="homeAsUpIndicator">@drawable/abs__ic_ab_back_holo_dark</item> + +        <item name="dividerVertical">@drawable/abs__list_divider_holo_dark</item> + +        <item name="spinnerDropDownItemStyle">@style/Widget.Sherlock.DropDownItem.Spinner</item> +        <item name="spinnerItemStyle">@style/Widget.Sherlock.TextView.SpinnerItem</item> + +        <item name="textColorPrimary">@color/abs__primary_text_holo_dark</item> +        <item name="textColorPrimaryDisableOnly">@color/abs__primary_text_disable_only_holo_dark</item> +        <item name="textColorPrimaryInverse">@color/abs__primary_text_holo_light</item> + +        <!-- Internal --><item name="dropdownListPreferredItemHeight">48dip</item> +        <item name="dropDownListViewStyle">@style/Widget.Sherlock.ListView.DropDown</item> + +        <item name="textAppearanceSmall">@style/TextAppearance.Sherlock.Small</item> +        <item name="textAppearanceLargePopupMenu">@style/TextAppearance.Sherlock.Widget.PopupMenu.Large</item> +        <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Sherlock.Widget.PopupMenu.Small</item> + +        <item name="popupMenuStyle">@style/Widget.Sherlock.PopupMenu</item> +        <!-- Internal --><item name="listPopupWindowStyle">@style/Widget.Sherlock.ListPopupWindow</item> + +        <item name="searchDropdownBackground">@drawable/abs__search_dropdown_dark</item> +        <item name="searchViewTextField">@drawable/abs__textfield_searchview_holo_dark</item> +        <item name="searchViewTextFieldRight">@drawable/abs__textfield_searchview_right_holo_dark</item> +        <item name="searchViewCloseIcon">@drawable/abs__ic_clear</item> +        <item name="searchViewSearchIcon">@drawable/abs__ic_search</item> +        <item name="searchViewGoIcon">@drawable/abs__ic_go</item> +        <item name="searchViewVoiceIcon">@drawable/abs__ic_voice_search</item> +        <item name="searchAutoCompleteTextView">@style/Widget.Sherlock.SearchAutoCompleteTextView</item> +        <item name="android:dropDownHintAppearance">@style/TextAppearance.Sherlock.Widget.DropDownHint</item> + +        <item name="windowActionBar">true</item> +        <item name="windowActionModeOverlay">false</item> +        <item name="windowContentOverlay">@null</item> +    </style> +    <style name="Theme.Sherlock.Light" parent="Sherlock.__Theme.Light"> +        <!-- Action bar styles (from Theme.Holo) --> +        <item name="actionDropDownStyle">@style/Widget.Sherlock.Light.Spinner.DropDown.ActionBar</item> +        <item name="actionButtonStyle">@style/Widget.Sherlock.Light.ActionButton</item> +        <item name="actionOverflowButtonStyle">@style/Widget.Sherlock.Light.ActionButton.Overflow</item> +        <item name="actionModeBackground">@drawable/abs__cab_background_top_holo_light</item> +        <item name="actionModeSplitBackground">@drawable/abs__cab_background_bottom_holo_light</item> +        <item name="actionModeCloseDrawable">@drawable/abs__ic_cab_done_holo_light</item> +        <item name="actionBarTabStyle">@style/Widget.Sherlock.Light.ActionBar.TabView</item> +        <item name="actionBarTabBarStyle">@style/Widget.Sherlock.Light.ActionBar.TabBar</item> +        <item name="actionBarTabTextStyle">@style/Widget.Sherlock.Light.ActionBar.TabText</item> +        <item name="actionModeStyle">@style/Widget.Sherlock.Light.ActionMode</item> +        <item name="actionModeCloseButtonStyle">@style/Widget.Sherlock.Light.ActionButton.CloseMode</item> +        <item name="actionBarStyle">@style/Widget.Sherlock.Light.ActionBar.Solid</item> +        <item name="actionBarSize">@dimen/abs__action_bar_default_height</item> +        <!-- Internal --><item name="actionModePopupWindowStyle">@style/Widget.Sherlock.Light.PopupWindow.ActionMode</item> +        <item name="actionBarWidgetTheme">@null</item> + +        <!-- Action bar styles (defaults from Theme) --> +        <item name="actionBarSplitStyle">?attr/actionBarStyle</item> +        <item name="actionMenuTextAppearance">@style/TextAppearance.Sherlock.Widget.ActionBar.Menu</item> +        <item name="actionMenuTextColor">?attr/textColorPrimary</item> +        <item name="actionBarDivider">?attr/dividerVertical</item> +        <item name="actionBarItemBackground">?attr/selectableItemBackground</item> + +        <item name="buttonStyleSmall">@style/Widget.Sherlock.Light.Button.Small</item> + +        <item name="selectableItemBackground">@drawable/abs__item_background_holo_light</item> + +        <item name="activatedBackgroundIndicator">@drawable/abs__activated_background_holo_light</item> +        <item name="actionModeShareDrawable">@drawable/abs__ic_menu_share_holo_light</item> +        <item name="activityChooserViewStyle">@style/Widget.Sherlock.Light.ActivityChooserView</item> + +        <item name="homeAsUpIndicator">@drawable/abs__ic_ab_back_holo_light</item> + +        <item name="dividerVertical">@drawable/abs__list_divider_holo_light</item> + +        <item name="spinnerDropDownItemStyle">@style/Widget.Sherlock.Light.DropDownItem.Spinner</item> +        <item name="spinnerItemStyle">@style/Widget.Sherlock.TextView.SpinnerItem</item> + +        <item name="textColorPrimary">@color/abs__primary_text_holo_light</item> +        <item name="textColorPrimaryDisableOnly">@color/abs__primary_text_disable_only_holo_light</item> +        <item name="textColorPrimaryInverse">@color/abs__primary_text_holo_dark</item> + +        <!-- Internal --><item name="dropdownListPreferredItemHeight">48dip</item> +        <item name="dropDownListViewStyle">@style/Widget.Sherlock.Light.ListView.DropDown</item> + +        <item name="textAppearanceSmall">@style/TextAppearance.Sherlock.Light.Small</item> +        <item name="textAppearanceLargePopupMenu">@style/TextAppearance.Sherlock.Light.Widget.PopupMenu.Large</item> +        <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Sherlock.Light.Widget.PopupMenu.Small</item> + +        <item name="popupMenuStyle">@style/Widget.Sherlock.Light.PopupMenu</item> +        <!-- Internal --><item name="listPopupWindowStyle">@style/Widget.Sherlock.Light.ListPopupWindow</item> + +        <item name="searchDropdownBackground">@drawable/abs__search_dropdown_light</item> +        <item name="searchViewTextField">@drawable/abs__textfield_searchview_holo_light</item> +        <item name="searchViewTextFieldRight">@drawable/abs__textfield_searchview_right_holo_light</item> +        <item name="searchViewCloseIcon">@drawable/abs__ic_clear_holo_light</item> +        <item name="searchViewSearchIcon">@drawable/abs__ic_search_api_holo_light</item> +        <item name="searchViewGoIcon">@drawable/abs__ic_go_search_api_holo_light</item> +        <item name="searchViewVoiceIcon">@drawable/abs__ic_voice_search_api_holo_light</item> +        <item name="searchAutoCompleteTextView">@style/Widget.Sherlock.Light.SearchAutoCompleteTextView</item> +        <item name="android:dropDownHintAppearance">@style/TextAppearance.Sherlock.Widget.DropDownHint</item> + +        <item name="windowActionBar">true</item> +        <item name="windowActionModeOverlay">false</item> +        <item name="windowContentOverlay">@null</item> +    </style> +    <style name="Theme.Sherlock.Light.DarkActionBar" parent="Sherlock.__Theme.DarkActionBar"> +        <item name="windowContentOverlay">@drawable/abs__ab_solid_shadow_holo</item> +        <item name="actionBarStyle">@style/Widget.Sherlock.Light.ActionBar.Solid.Inverse</item> +        <item name="actionBarWidgetTheme">@style/Theme.Sherlock</item> + +        <item name="actionDropDownStyle">@style/Widget.Sherlock.Spinner.DropDown.ActionBar</item> +        <item name="actionButtonStyle">@style/Widget.Sherlock.ActionButton</item> +        <item name="actionOverflowButtonStyle">@style/Widget.Sherlock.ActionButton.Overflow</item> +        <item name="actionModeBackground">@drawable/abs__cab_background_top_holo_dark</item> +        <item name="actionModeSplitBackground">@drawable/abs__cab_background_bottom_holo_dark</item> +        <item name="actionModeCloseDrawable">@drawable/abs__ic_cab_done_holo_dark</item> +        <item name="homeAsUpIndicator">@drawable/abs__ic_ab_back_holo_dark</item> +        <item name="actionBarTabStyle">@style/Widget.Sherlock.Light.ActionBar.TabView.Inverse</item> +        <item name="actionBarTabBarStyle">@style/Widget.Sherlock.Light.ActionBar.TabBar.Inverse</item> +        <item name="actionBarTabTextStyle">@style/Widget.Sherlock.Light.ActionBar.TabText.Inverse</item> +        <item name="actionBarDivider">@drawable/abs__list_divider_holo_dark</item> +        <item name="actionBarItemBackground">@drawable/abs__item_background_holo_dark</item> +        <item name="actionMenuTextColor">?attr/textColorPrimaryInverse</item> +        <item name="actionModeStyle">@style/Widget.Sherlock.Light.ActionMode.Inverse</item> +        <item name="actionModeCloseButtonStyle">@style/Widget.Sherlock.ActionButton.CloseMode</item> +        <item name="actionModePopupWindowStyle">@style/Widget.Sherlock.PopupWindow.ActionMode</item> + +        <item name="actionModeShareDrawable">@drawable/abs__ic_menu_share_holo_dark</item> +    </style> + + +    <style name="Theme.Sherlock.NoActionBar"> +        <item name="windowActionBar">false</item> +        <item name="windowNoTitle">true</item> +    </style> +    <style name="Theme.Sherlock.Light.NoActionBar"> +        <item name="windowActionBar">false</item> +        <item name="windowNoTitle">true</item> +    </style> + + +    <style name="Theme.Sherlock.Dialog" parent="android:Theme"> +        <item name="android:windowFrame">@null</item> +        <item name="android:windowTitleStyle">@style/DialogWindowTitle.Sherlock</item> +        <item name="android:windowBackground">@drawable/abs__dialog_full_holo_dark</item> +        <item name="android:windowIsFloating">true</item> +        <item name="android:windowContentOverlay">@null</item> +        <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item> +        <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item> + +        <item name="android:windowActionBar">false</item> +        <item name="android:windowActionModeOverlay">true</item> +        <item name="android:windowCloseOnTouchOutside">true</item> +        <item name="android:windowNoTitle">true</item> +        <item name="android:backgroundDimAmount">0.6</item> + +        <item name="android:colorBackgroundCacheHint">@null</item> + +        <item name="android:textColorPrimary">@color/abs__primary_text_holo_dark</item> +        <item name="android:textColorPrimaryInverse">@color/abs__primary_text_holo_light</item> + +        <item name="windowMinWidthMajor">@dimen/abs__dialog_min_width_major</item> +        <item name="windowMinWidthMinor">@dimen/abs__dialog_min_width_minor</item> + +        <item name="windowActionBar">false</item> +        <item name="windowContentOverlay">@null</item> +    </style> +    <style name="Theme.Sherlock.Light.Dialog" parent="android:Theme.Light"> +        <item name="android:windowFrame">@null</item> +        <item name="android:windowTitleStyle">@style/DialogWindowTitle.Sherlock.Light</item> +        <item name="android:windowBackground">@drawable/abs__dialog_full_holo_light</item> +        <item name="android:windowIsFloating">true</item> +        <item name="android:windowContentOverlay">@null</item> +        <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item> +        <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item> + +        <item name="android:windowActionBar">false</item> +        <item name="android:windowActionModeOverlay">true</item> +        <item name="android:windowCloseOnTouchOutside">true</item> +        <item name="android:windowNoTitle">true</item> +        <item name="android:backgroundDimAmount">0.6</item> + +        <item name="android:colorBackgroundCacheHint">@null</item> + +        <item name="android:textColorPrimary">@color/abs__primary_text_holo_light</item> +        <item name="android:textColorPrimaryInverse">@color/abs__primary_text_holo_dark</item> + +        <item name="windowMinWidthMajor">@dimen/abs__dialog_min_width_major</item> +        <item name="windowMinWidthMinor">@dimen/abs__dialog_min_width_minor</item> + +        <item name="windowActionBar">false</item> +        <item name="windowContentOverlay">@null</item> +    </style> +</resources> diff --git a/libraries/ActionBarSherlock/src/android/support/v4/app/Watson.java b/libraries/ActionBarSherlock/src/android/support/v4/app/Watson.java new file mode 100644 index 000000000..d93de4c6a --- /dev/null +++ b/libraries/ActionBarSherlock/src/android/support/v4/app/Watson.java @@ -0,0 +1,144 @@ +package android.support.v4.app; + +import android.util.Log; +import android.view.View; +import android.view.Window; +import com.actionbarsherlock.ActionBarSherlock.OnCreatePanelMenuListener; +import com.actionbarsherlock.ActionBarSherlock.OnMenuItemSelectedListener; +import com.actionbarsherlock.ActionBarSherlock.OnPreparePanelListener; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuInflater; +import com.actionbarsherlock.view.MenuItem; + +import java.util.ArrayList; + +/** I'm in ur package. Stealing ur variables. */ +public abstract class Watson extends FragmentActivity implements OnCreatePanelMenuListener, OnPreparePanelListener, OnMenuItemSelectedListener { +    private static final boolean DEBUG = false; +    private static final String TAG = "Watson"; + +    /** Fragment interface for menu creation callback. */ +    public interface OnCreateOptionsMenuListener { +        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater); +    } +    /** Fragment interface for menu preparation callback. */ +    public interface OnPrepareOptionsMenuListener { +        public void onPrepareOptionsMenu(Menu menu); +    } +    /** Fragment interface for menu item selection callback. */ +    public interface OnOptionsItemSelectedListener { +        public boolean onOptionsItemSelected(MenuItem item); +    } + +    private ArrayList<Fragment> mCreatedMenus; + + +    /////////////////////////////////////////////////////////////////////////// +    // Sherlock menu handling +    /////////////////////////////////////////////////////////////////////////// + +    @Override +    public boolean onCreatePanelMenu(int featureId, Menu menu) { +        if (DEBUG) Log.d(TAG, "[onCreatePanelMenu] featureId: " + featureId + ", menu: " + menu); + +        if (featureId == Window.FEATURE_OPTIONS_PANEL) { +            boolean result = onCreateOptionsMenu(menu); +            if (DEBUG) Log.d(TAG, "[onCreatePanelMenu] activity create result: " + result); + +            MenuInflater inflater = getSupportMenuInflater(); +            boolean show = false; +            ArrayList<Fragment> newMenus = null; +            if (mFragments.mAdded != null) { +                for (int i = 0; i < mFragments.mAdded.size(); i++) { +                    Fragment f = mFragments.mAdded.get(i); +                    if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible && f instanceof OnCreateOptionsMenuListener) { +                        show = true; +                        ((OnCreateOptionsMenuListener)f).onCreateOptionsMenu(menu, inflater); +                        if (newMenus == null) { +                            newMenus = new ArrayList<Fragment>(); +                        } +                        newMenus.add(f); +                    } +                } +            } + +            if (mCreatedMenus != null) { +                for (int i = 0; i < mCreatedMenus.size(); i++) { +                    Fragment f = mCreatedMenus.get(i); +                    if (newMenus == null || !newMenus.contains(f)) { +                        f.onDestroyOptionsMenu(); +                    } +                } +            } + +            mCreatedMenus = newMenus; + +            if (DEBUG) Log.d(TAG, "[onCreatePanelMenu] fragments create result: " + show); +            result |= show; + +            if (DEBUG) Log.d(TAG, "[onCreatePanelMenu] returning " + result); +            return result; +        } +        return false; +    } + +    @Override +    public boolean onPreparePanel(int featureId, View view, Menu menu) { +        if (DEBUG) Log.d(TAG, "[onPreparePanel] featureId: " + featureId + ", view: " + view + " menu: " + menu); + +        if (featureId == Window.FEATURE_OPTIONS_PANEL) { +            boolean result = onPrepareOptionsMenu(menu); +            if (DEBUG) Log.d(TAG, "[onPreparePanel] activity prepare result: " + result); + +            boolean show = false; +            if (mFragments.mAdded != null) { +                for (int i = 0; i < mFragments.mAdded.size(); i++) { +                    Fragment f = mFragments.mAdded.get(i); +                    if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible && f instanceof OnPrepareOptionsMenuListener) { +                        show = true; +                        ((OnPrepareOptionsMenuListener)f).onPrepareOptionsMenu(menu); +                    } +                } +            } + +            if (DEBUG) Log.d(TAG, "[onPreparePanel] fragments prepare result: " + show); +            result |= show; + +            result &= menu.hasVisibleItems(); +            if (DEBUG) Log.d(TAG, "[onPreparePanel] returning " + result); +            return result; +        } +        return false; +    } + +    @Override +    public boolean onMenuItemSelected(int featureId, MenuItem item) { +        if (DEBUG) Log.d(TAG, "[onMenuItemSelected] featureId: " + featureId + ", item: " + item); + +        if (featureId == Window.FEATURE_OPTIONS_PANEL) { +            if (onOptionsItemSelected(item)) { +                return true; +            } + +            if (mFragments.mAdded != null) { +                for (int i = 0; i < mFragments.mAdded.size(); i++) { +                    Fragment f = mFragments.mAdded.get(i); +                    if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible && f instanceof OnOptionsItemSelectedListener) { +                        if (((OnOptionsItemSelectedListener)f).onOptionsItemSelected(item)) { +                            return true; +                        } +                    } +                } +            } +        } +        return false; +    } + +    public abstract boolean onCreateOptionsMenu(Menu menu); + +    public abstract boolean onPrepareOptionsMenu(Menu menu); + +    public abstract boolean onOptionsItemSelected(MenuItem item); + +    public abstract MenuInflater getSupportMenuInflater(); +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/ActionBarSherlock.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/ActionBarSherlock.java new file mode 100644 index 000000000..ab160f836 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/ActionBarSherlock.java @@ -0,0 +1,794 @@ +package com.actionbarsherlock;
 +
 +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 +import java.lang.annotation.ElementType;
 +import java.lang.annotation.Retention;
 +import java.lang.annotation.RetentionPolicy;
 +import java.lang.annotation.Target;
 +import java.lang.reflect.Constructor;
 +import java.lang.reflect.InvocationTargetException;
 +import java.util.HashMap;
 +import java.util.Iterator;
 +import android.app.Activity;
 +import android.content.Context;
 +import android.content.res.Configuration;
 +import android.os.Build;
 +import android.os.Bundle;
 +import android.util.DisplayMetrics;
 +import android.util.Log;
 +import android.view.KeyEvent;
 +import android.view.View;
 +import android.view.ViewGroup;
 +import android.view.Window;
 +import com.actionbarsherlock.app.ActionBar;
 +import com.actionbarsherlock.internal.ActionBarSherlockCompat;
 +import com.actionbarsherlock.internal.ActionBarSherlockNative;
 +import com.actionbarsherlock.view.ActionMode;
 +import com.actionbarsherlock.view.Menu;
 +import com.actionbarsherlock.view.MenuInflater;
 +import com.actionbarsherlock.view.MenuItem;
 +
 +/**
 + * <p>Helper for implementing the action bar design pattern across all versions
 + * of Android.</p>
 + *
 + * <p>This class will manage interaction with a custom action bar based on the
 + * Android 4.0 source code. The exposed API mirrors that of its native
 + * counterpart and you should refer to its documentation for instruction.</p>
 + *
 + * @author Jake Wharton <jakewharton@gmail.com>
 + */
 +public abstract class ActionBarSherlock {
 +    protected static final String TAG = "ActionBarSherlock";
 +    protected static final boolean DEBUG = false;
 +
 +    private static final Class<?>[] CONSTRUCTOR_ARGS = new Class[] { Activity.class, int.class };
 +    private static final HashMap<Implementation, Class<? extends ActionBarSherlock>> IMPLEMENTATIONS =
 +            new HashMap<Implementation, Class<? extends ActionBarSherlock>>();
 +
 +    static {
 +        //Register our two built-in implementations
 +        registerImplementation(ActionBarSherlockCompat.class);
 +        registerImplementation(ActionBarSherlockNative.class);
 +    }
 +
 +
 +    /**
 +     * <p>Denotes an implementation of ActionBarSherlock which provides an
 +     * action bar-enhanced experience.</p>
 +     */
 +    @Target(ElementType.TYPE)
 +    @Retention(RetentionPolicy.RUNTIME)
 +    public @interface Implementation {
 +        static final int DEFAULT_API = -1;
 +        static final int DEFAULT_DPI = -1;
 +
 +        int api() default DEFAULT_API;
 +        int dpi() default DEFAULT_DPI;
 +    }
 +
 +
 +    /** Activity interface for menu creation callback. */
 +    public interface OnCreatePanelMenuListener {
 +        public boolean onCreatePanelMenu(int featureId, Menu menu);
 +    }
 +    /** Activity interface for menu creation callback. */
 +    public interface OnCreateOptionsMenuListener {
 +        public boolean onCreateOptionsMenu(Menu menu);
 +    }
 +    /** Activity interface for menu item selection callback. */
 +    public interface OnMenuItemSelectedListener {
 +        public boolean onMenuItemSelected(int featureId, MenuItem item);
 +    }
 +    /** Activity interface for menu item selection callback. */
 +    public interface OnOptionsItemSelectedListener {
 +        public boolean onOptionsItemSelected(MenuItem item);
 +    }
 +    /** Activity interface for menu preparation callback. */
 +    public interface OnPreparePanelListener {
 +        public boolean onPreparePanel(int featureId, View view, Menu menu);
 +    }
 +    /** Activity interface for menu preparation callback. */
 +    public interface OnPrepareOptionsMenuListener {
 +        public boolean onPrepareOptionsMenu(Menu menu);
 +    }
 +    /** Activity interface for action mode finished callback. */
 +    public interface OnActionModeFinishedListener {
 +        public void onActionModeFinished(ActionMode mode);
 +    }
 +    /** Activity interface for action mode started callback. */
 +    public interface OnActionModeStartedListener {
 +        public void onActionModeStarted(ActionMode mode);
 +    }
 +
 +
 +    /**
 +     * If set, the logic in these classes will assume that an {@link Activity}
 +     * is dispatching all of the required events to the class. This flag should
 +     * only be used internally or if you are creating your own base activity
 +     * modeled after one of the included types (e.g., {@code SherlockActivity}).
 +     */
 +    public static final int FLAG_DELEGATE = 1;
 +
 +
 +    /**
 +     * Register an ActionBarSherlock implementation.
 +     *
 +     * @param implementationClass Target implementation class which extends
 +     * {@link ActionBarSherlock}. This class must also be annotated with
 +     * {@link Implementation}.
 +     */
 +    public static void registerImplementation(Class<? extends ActionBarSherlock> implementationClass) {
 +        if (!implementationClass.isAnnotationPresent(Implementation.class)) {
 +            throw new IllegalArgumentException("Class " + implementationClass.getSimpleName() + " is not annotated with @Implementation");
 +        } else if (IMPLEMENTATIONS.containsValue(implementationClass)) {
 +            if (DEBUG) Log.w(TAG, "Class " + implementationClass.getSimpleName() + " already registered");
 +            return;
 +        }
 +
 +        Implementation impl = implementationClass.getAnnotation(Implementation.class);
 +        if (DEBUG) Log.i(TAG, "Registering " + implementationClass.getSimpleName() + " with qualifier " + impl);
 +        IMPLEMENTATIONS.put(impl, implementationClass);
 +    }
 +
 +    /**
 +     * Unregister an ActionBarSherlock implementation. <strong>This should be
 +     * considered very volatile and you should only use it if you know what
 +     * you are doing.</strong> You have been warned.
 +     *
 +     * @param implementationClass Target implementation class.
 +     * @return Boolean indicating whether the class was removed.
 +     */
 +    public static boolean unregisterImplementation(Class<? extends ActionBarSherlock> implementationClass) {
 +        return IMPLEMENTATIONS.values().remove(implementationClass);
 +    }
 +
 +    /**
 +     * Wrap an activity with an action bar abstraction which will enable the
 +     * use of a custom implementation on platforms where a native version does
 +     * not exist.
 +     *
 +     * @param activity Activity to wrap.
 +     * @return Instance to interact with the action bar.
 +     */
 +    public static ActionBarSherlock wrap(Activity activity) {
 +        return wrap(activity, 0);
 +    }
 +
 +    /**
 +     * Wrap an activity with an action bar abstraction which will enable the
 +     * use of a custom implementation on platforms where a native version does
 +     * not exist.
 +     *
 +     * @param activity Owning activity.
 +     * @param flags Option flags to control behavior.
 +     * @return Instance to interact with the action bar.
 +     */
 +    public static ActionBarSherlock wrap(Activity activity, int flags) {
 +        //Create a local implementation map we can modify
 +        HashMap<Implementation, Class<? extends ActionBarSherlock>> impls =
 +                new HashMap<Implementation, Class<? extends ActionBarSherlock>>(IMPLEMENTATIONS);
 +        boolean hasQualfier;
 +
 +        /* DPI FILTERING */
 +        hasQualfier = false;
 +        for (Implementation key : impls.keySet()) {
 +            //Only honor TVDPI as a specific qualifier
 +            if (key.dpi() == DisplayMetrics.DENSITY_TV) {
 +                hasQualfier = true;
 +                break;
 +            }
 +        }
 +        if (hasQualfier) {
 +            final boolean isTvDpi = activity.getResources().getDisplayMetrics().densityDpi == DisplayMetrics.DENSITY_TV;
 +            for (Iterator<Implementation> keys = impls.keySet().iterator(); keys.hasNext(); ) {
 +                int keyDpi = keys.next().dpi();
 +                if ((isTvDpi && keyDpi != DisplayMetrics.DENSITY_TV)
 +                        || (!isTvDpi && keyDpi == DisplayMetrics.DENSITY_TV)) {
 +                    keys.remove();
 +                }
 +            }
 +        }
 +
 +        /* API FILTERING */
 +        hasQualfier = false;
 +        for (Implementation key : impls.keySet()) {
 +            if (key.api() != Implementation.DEFAULT_API) {
 +                hasQualfier = true;
 +                break;
 +            }
 +        }
 +        if (hasQualfier) {
 +            final int runtimeApi = Build.VERSION.SDK_INT;
 +            int bestApi = 0;
 +            for (Iterator<Implementation> keys = impls.keySet().iterator(); keys.hasNext(); ) {
 +                int keyApi = keys.next().api();
 +                if (keyApi > runtimeApi) {
 +                    keys.remove();
 +                } else if (keyApi > bestApi) {
 +                    bestApi = keyApi;
 +                }
 +            }
 +            for (Iterator<Implementation> keys = impls.keySet().iterator(); keys.hasNext(); ) {
 +                if (keys.next().api() != bestApi) {
 +                    keys.remove();
 +                }
 +            }
 +        }
 +
 +        if (impls.size() > 1) {
 +            throw new IllegalStateException("More than one implementation matches configuration.");
 +        }
 +        if (impls.isEmpty()) {
 +            throw new IllegalStateException("No implementations match configuration.");
 +        }
 +        Class<? extends ActionBarSherlock> impl = impls.values().iterator().next();
 +        if (DEBUG) Log.i(TAG, "Using implementation: " + impl.getSimpleName());
 +
 +        try {
 +            Constructor<? extends ActionBarSherlock> ctor = impl.getConstructor(CONSTRUCTOR_ARGS);
 +            return ctor.newInstance(activity, flags);
 +        } catch (NoSuchMethodException e) {
 +            throw new RuntimeException(e);
 +        } catch (IllegalArgumentException e) {
 +            throw new RuntimeException(e);
 +        } catch (InstantiationException e) {
 +            throw new RuntimeException(e);
 +        } catch (IllegalAccessException e) {
 +            throw new RuntimeException(e);
 +        } catch (InvocationTargetException e) {
 +            throw new RuntimeException(e);
 +        }
 +    }
 +
 +
 +    /** Activity which is displaying the action bar. Also used for context. */
 +    protected final Activity mActivity;
 +    /** Whether delegating actions for the activity or managing ourselves. */
 +    protected final boolean mIsDelegate;
 +
 +    /** Reference to our custom menu inflater which supports action items. */
 +    protected MenuInflater mMenuInflater;
 +
 +
 +
 +    protected ActionBarSherlock(Activity activity, int flags) {
 +        if (DEBUG) Log.d(TAG, "[<ctor>] activity: " + activity + ", flags: " + flags);
 +
 +        mActivity = activity;
 +        mIsDelegate = (flags & FLAG_DELEGATE) != 0;
 +    }
 +
 +
 +    /**
 +     * Get the current action bar instance.
 +     *
 +     * @return Action bar instance.
 +     */
 +    public abstract ActionBar getActionBar();
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Lifecycle and interaction callbacks when delegating
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    /**
 +     * Notify action bar of a configuration change event. Should be dispatched
 +     * after the call to the superclass implementation.
 +     *
 +     * <blockquote><pre>
 +     * @Override
 +     * public void onConfigurationChanged(Configuration newConfig) {
 +     *     super.onConfigurationChanged(newConfig);
 +     *     mSherlock.dispatchConfigurationChanged(newConfig);
 +     * }
 +     * </pre></blockquote>
 +     *
 +     * @param newConfig The new device configuration.
 +     */
 +    public void dispatchConfigurationChanged(Configuration newConfig) {}
 +
 +    /**
 +     * Notify the action bar that the activity has finished its resuming. This
 +     * should be dispatched after the call to the superclass implementation.
 +     *
 +     * <blockquote><pre>
 +     * @Override
 +     * protected void onPostResume() {
 +     *     super.onPostResume();
 +     *     mSherlock.dispatchPostResume();
 +     * }
 +     * </pre></blockquote>
 +     */
 +    public void dispatchPostResume() {}
 +
 +    /**
 +     * Notify the action bar that the activity is pausing. This should be
 +     * dispatched before the call to the superclass implementation.
 +     *
 +     * <blockquote><pre>
 +     * @Override
 +     * protected void onPause() {
 +     *     mSherlock.dispatchPause();
 +     *     super.onPause();
 +     * }
 +     * </pre></blockquote>
 +     */
 +    public void dispatchPause() {}
 +
 +    /**
 +     * Notify the action bar that the activity is stopping. This should be
 +     * called before the superclass implementation.
 +     *
 +     * <blockquote><p>
 +     * @Override
 +     * protected void onStop() {
 +     *     mSherlock.dispatchStop();
 +     *     super.onStop();
 +     * }
 +     * </p></blockquote>
 +     */
 +    public void dispatchStop() {}
 +
 +    /**
 +     * Indicate that the menu should be recreated by calling
 +     * {@link OnCreateOptionsMenuListener#onCreateOptionsMenu(com.actionbarsherlock.view.Menu)}.
 +     */
 +    public abstract void dispatchInvalidateOptionsMenu();
 +
 +    /**
 +     * Notify the action bar that it should display its overflow menu if it is
 +     * appropriate for the device. The implementation should conditionally
 +     * call the superclass method only if this method returns {@code false}.
 +     *
 +     * <blockquote><p>
 +     * @Override
 +     * public void openOptionsMenu() {
 +     *     if (!mSherlock.dispatchOpenOptionsMenu()) {
 +     *         super.openOptionsMenu();
 +     *     }
 +     * }
 +     * </p></blockquote>
 +     *
 +     * @return {@code true} if the opening of the menu was handled internally.
 +     */
 +    public boolean dispatchOpenOptionsMenu() {
 +        return false;
 +    }
 +
 +    /**
 +     * Notify the action bar that it should close its overflow menu if it is
 +     * appropriate for the device. This implementation should conditionally
 +     * call the superclass method only if this method returns {@code false}.
 +     *
 +     * <blockquote><pre>
 +     * @Override
 +     * public void closeOptionsMenu() {
 +     *     if (!mSherlock.dispatchCloseOptionsMenu()) {
 +     *         super.closeOptionsMenu();
 +     *     }
 +     * }
 +     * </pre></blockquote>
 +     *
 +     * @return {@code true} if the closing of the menu was handled internally.
 +     */
 +    public boolean dispatchCloseOptionsMenu() {
 +        return false;
 +    }
 +
 +    /**
 +     * Notify the class that the activity has finished its creation. This
 +     * should be called after the superclass implementation.
 +     *
 +     * <blockquote><pre>
 +     * @Override
 +     * protected void onPostCreate(Bundle savedInstanceState) {
 +     *     mSherlock.dispatchPostCreate(savedInstanceState);
 +     *     super.onPostCreate(savedInstanceState);
 +     * }
 +     * </pre></blockquote>
 +     *
 +     * @param savedInstanceState If the activity is being re-initialized after
 +     *                           previously being shut down then this Bundle
 +     *                           contains the data it most recently supplied in
 +     *                           {@link Activity#}onSaveInstanceState(Bundle)}.
 +     *                           <strong>Note: Otherwise it is null.</strong>
 +     */
 +    public void dispatchPostCreate(Bundle savedInstanceState) {}
 +
 +    /**
 +     * Notify the action bar that the title has changed and the action bar
 +     * should be updated to reflect the change. This should be called before
 +     * the superclass implementation.
 +     *
 +     * <blockquote><pre>
 +     *  @Override
 +     *  protected void onTitleChanged(CharSequence title, int color) {
 +     *      mSherlock.dispatchTitleChanged(title, color);
 +     *      super.onTitleChanged(title, color);
 +     *  }
 +     * </pre></blockquote>
 +     *
 +     * @param title New activity title.
 +     * @param color New activity color.
 +     */
 +    public void dispatchTitleChanged(CharSequence title, int color) {}
 +
 +    /**
 +     * Notify the action bar the user has created a key event. This is used to
 +     * toggle the display of the overflow action item with the menu key and to
 +     * close the action mode or expanded action item with the back key.
 +     *
 +     * <blockquote><pre>
 +     * @Override
 +     * public boolean dispatchKeyEvent(KeyEvent event) {
 +     *     if (mSherlock.dispatchKeyEvent(event)) {
 +     *         return true;
 +     *     }
 +     *     return super.dispatchKeyEvent(event);
 +     * }
 +     * </pre></blockquote>
 +     *
 +     * @param event Description of the key event.
 +     * @return {@code true} if the event was handled.
 +     */
 +    public boolean dispatchKeyEvent(KeyEvent event) {
 +        return false;
 +    }
 +
 +    /**
 +     * Notify the action bar that the Activity has triggered a menu creation
 +     * which should happen on the conclusion of {@link Activity#onCreate}. This
 +     * will be used to gain a reference to the native menu for native and
 +     * overflow binding as well as to indicate when compatibility create should
 +     * occur for the first time.
 +     *
 +     * @param menu Activity native menu.
 +     * @return {@code true} since we always want to say that we have a native
 +     */
 +    public abstract boolean dispatchCreateOptionsMenu(android.view.Menu menu);
 +
 +    /**
 +     * Notify the action bar that the Activity has triggered a menu preparation
 +     * which usually means that the user has requested the overflow menu via a
 +     * hardware menu key. You should return the result of this method call and
 +     * not call the superclass implementation.
 +     *
 +     * <blockquote><p>
 +     * @Override
 +     * public final boolean onPrepareOptionsMenu(android.view.Menu menu) {
 +     *     return mSherlock.dispatchPrepareOptionsMenu(menu);
 +     * }
 +     * </p></blockquote>
 +     *
 +     * @param menu Activity native menu.
 +     * @return {@code true} if menu display should proceed.
 +     */
 +    public abstract boolean dispatchPrepareOptionsMenu(android.view.Menu menu);
 +
 +    /**
 +     * Notify the action bar that a native options menu item has been selected.
 +     * The implementation should return the result of this method call.
 +     *
 +     * <blockquote><p>
 +     * @Override
 +     * public final boolean onOptionsItemSelected(android.view.MenuItem item) {
 +     *     return mSherlock.dispatchOptionsItemSelected(item);
 +     * }
 +     * </p></blockquote>
 +     *
 +     * @param item Options menu item.
 +     * @return @{code true} if the selection was handled.
 +     */
 +    public abstract boolean dispatchOptionsItemSelected(android.view.MenuItem item);
 +
 +    /**
 +     * Notify the action bar that the overflow menu has been opened. The
 +     * implementation should conditionally return {@code true} if this method
 +     * returns {@code true}, otherwise return the result of the superclass
 +     * method.
 +     *
 +     * <blockquote><p>
 +     * @Override
 +     * public final boolean onMenuOpened(int featureId, android.view.Menu menu) {
 +     *     if (mSherlock.dispatchMenuOpened(featureId, menu)) {
 +     *         return true;
 +     *     }
 +     *     return super.onMenuOpened(featureId, menu);
 +     * }
 +     * </p></blockquote>
 +     *
 +     * @param featureId Window feature which triggered the event.
 +     * @param menu Activity native menu.
 +     * @return {@code true} if the event was handled by this method.
 +     */
 +    public boolean dispatchMenuOpened(int featureId, android.view.Menu menu) {
 +        return false;
 +    }
 +
 +    /**
 +     * Notify the action bar that the overflow menu has been closed. This
 +     * method should be called before the superclass implementation.
 +     *
 +     * <blockquote><p>
 +     * @Override
 +     * public void onPanelClosed(int featureId, android.view.Menu menu) {
 +     *     mSherlock.dispatchPanelClosed(featureId, menu);
 +     *     super.onPanelClosed(featureId, menu);
 +     * }
 +     * </p></blockquote>
 +     *
 +     * @param featureId
 +     * @param menu
 +     */
 +    public void dispatchPanelClosed(int featureId, android.view.Menu menu) {}
 +
 +    /**
 +     * Notify the action bar that the activity has been destroyed. This method
 +     * should be called before the superclass implementation.
 +     *
 +     * <blockquote><p>
 +     * @Override
 +     * public void onDestroy() {
 +     *     mSherlock.dispatchDestroy();
 +     *     super.onDestroy();
 +     * }
 +     * </p></blockquote>
 +     */
 +    public void dispatchDestroy() {}
 +
 +    public void dispatchSaveInstanceState(Bundle outState) {}
 +
 +    public void dispatchRestoreInstanceState(Bundle savedInstanceState) {}
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +
 +    /**
 +     * Internal method to trigger the menu creation process.
 +     *
 +     * @return {@code true} if menu creation should proceed.
 +     */
 +    protected final boolean callbackCreateOptionsMenu(Menu menu) {
 +        if (DEBUG) Log.d(TAG, "[callbackCreateOptionsMenu] menu: " + menu);
 +
 +        boolean result = true;
 +        if (mActivity instanceof OnCreatePanelMenuListener) {
 +            OnCreatePanelMenuListener listener = (OnCreatePanelMenuListener)mActivity;
 +            result = listener.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu);
 +        } else if (mActivity instanceof OnCreateOptionsMenuListener) {
 +            OnCreateOptionsMenuListener listener = (OnCreateOptionsMenuListener)mActivity;
 +            result = listener.onCreateOptionsMenu(menu);
 +        }
 +
 +        if (DEBUG) Log.d(TAG, "[callbackCreateOptionsMenu] returning " + result);
 +        return result;
 +    }
 +
 +    /**
 +     * Internal method to trigger the menu preparation process.
 +     *
 +     * @return {@code true} if menu preparation should proceed.
 +     */
 +    protected final boolean callbackPrepareOptionsMenu(Menu menu) {
 +        if (DEBUG) Log.d(TAG, "[callbackPrepareOptionsMenu] menu: " + menu);
 +
 +        boolean result = true;
 +        if (mActivity instanceof OnPreparePanelListener) {
 +            OnPreparePanelListener listener = (OnPreparePanelListener)mActivity;
 +            result = listener.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu);
 +        } else if (mActivity instanceof OnPrepareOptionsMenuListener) {
 +            OnPrepareOptionsMenuListener listener = (OnPrepareOptionsMenuListener)mActivity;
 +            result = listener.onPrepareOptionsMenu(menu);
 +        }
 +
 +        if (DEBUG) Log.d(TAG, "[callbackPrepareOptionsMenu] returning " + result);
 +        return result;
 +    }
 +
 +    /**
 +     * Internal method for dispatching options menu selection to the owning
 +     * activity callback.
 +     *
 +     * @param item Selected options menu item.
 +     * @return {@code true} if the item selection was handled in the callback.
 +     */
 +    protected final boolean callbackOptionsItemSelected(MenuItem item) {
 +        if (DEBUG) Log.d(TAG, "[callbackOptionsItemSelected] item: " + item.getTitleCondensed());
 +
 +        boolean result = false;
 +        if (mActivity instanceof OnMenuItemSelectedListener) {
 +            OnMenuItemSelectedListener listener = (OnMenuItemSelectedListener)mActivity;
 +            result = listener.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
 +        } else if (mActivity instanceof OnOptionsItemSelectedListener) {
 +            OnOptionsItemSelectedListener listener = (OnOptionsItemSelectedListener)mActivity;
 +            result = listener.onOptionsItemSelected(item);
 +        }
 +
 +        if (DEBUG) Log.d(TAG, "[callbackOptionsItemSelected] returning " + result);
 +        return result;
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +
 +    /**
 +     * Query for the availability of a certain feature.
 +     *
 +     * @param featureId The feature ID to check.
 +     * @return {@code true} if feature is enabled, {@code false} otherwise.
 +     */
 +    public abstract boolean hasFeature(int featureId);
 +
 +    /**
 +     * Enable extended screen features. This must be called before
 +     * {@code setContentView()}. May be called as many times as desired as long
 +     * as it is before {@code setContentView()}. If not called, no extended
 +     * features will be available. You can not turn off a feature once it is
 +     * requested.
 +     *
 +     * @param featureId The desired features, defined as constants by Window.
 +     * @return Returns true if the requested feature is supported and now
 +     * enabled.
 +     */
 +    public abstract boolean requestFeature(int featureId);
 +
 +    /**
 +     * Set extra options that will influence the UI for this window.
 +     *
 +     * @param uiOptions Flags specifying extra options for this window.
 +     */
 +    public abstract void setUiOptions(int uiOptions);
 +
 +    /**
 +     * Set extra options that will influence the UI for this window. Only the
 +     * bits filtered by mask will be modified.
 +     *
 +     * @param uiOptions Flags specifying extra options for this window.
 +     * @param mask Flags specifying which options should be modified. Others
 +     *             will remain unchanged.
 +     */
 +    public abstract void setUiOptions(int uiOptions, int mask);
 +
 +    /**
 +     * Set the content of the activity inside the action bar.
 +     *
 +     * @param layoutResId Layout resource ID.
 +     */
 +    public abstract void setContentView(int layoutResId);
 +
 +    /**
 +     * Set the content of the activity inside the action bar.
 +     *
 +     * @param view The desired content to display.
 +     */
 +    public void setContentView(View view) {
 +        if (DEBUG) Log.d(TAG, "[setContentView] view: " + view);
 +
 +        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
 +    }
 +
 +    /**
 +     * Set the content of the activity inside the action bar.
 +     *
 +     * @param view The desired content to display.
 +     * @param params Layout parameters to apply to the view.
 +     */
 +    public abstract void setContentView(View view, ViewGroup.LayoutParams params);
 +
 +    /**
 +     * Variation on {@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}
 +     * to add an additional content view to the screen. Added after any
 +     * existing ones on the screen -- existing views are NOT removed.
 +     *
 +     * @param view The desired content to display.
 +     * @param params Layout parameters for the view.
 +     */
 +    public abstract void addContentView(View view, ViewGroup.LayoutParams params);
 +
 +    /**
 +     * Change the title associated with this activity.
 +     */
 +    public abstract void setTitle(CharSequence title);
 +
 +    /**
 +     * Change the title associated with this activity.
 +     */
 +    public void setTitle(int resId) {
 +        if (DEBUG) Log.d(TAG, "[setTitle] resId: " + resId);
 +
 +        setTitle(mActivity.getString(resId));
 +    }
 +
 +    /**
 +     * Sets the visibility of the progress bar in the title.
 +     * <p>
 +     * In order for the progress bar to be shown, the feature must be requested
 +     * via {@link #requestWindowFeature(int)}.
 +     *
 +     * @param visible Whether to show the progress bars in the title.
 +     */
 +    public abstract void setProgressBarVisibility(boolean visible);
 +
 +    /**
 +     * Sets the visibility of the indeterminate progress bar in the title.
 +     * <p>
 +     * In order for the progress bar to be shown, the feature must be requested
 +     * via {@link #requestWindowFeature(int)}.
 +     *
 +     * @param visible Whether to show the progress bars in the title.
 +     */
 +    public abstract void setProgressBarIndeterminateVisibility(boolean visible);
 +
 +    /**
 +     * Sets whether the horizontal progress bar in the title should be indeterminate (the circular
 +     * is always indeterminate).
 +     * <p>
 +     * In order for the progress bar to be shown, the feature must be requested
 +     * via {@link #requestWindowFeature(int)}.
 +     *
 +     * @param indeterminate Whether the horizontal progress bar should be indeterminate.
 +     */
 +    public abstract void setProgressBarIndeterminate(boolean indeterminate);
 +
 +    /**
 +     * Sets the progress for the progress bars in the title.
 +     * <p>
 +     * In order for the progress bar to be shown, the feature must be requested
 +     * via {@link #requestWindowFeature(int)}.
 +     *
 +     * @param progress The progress for the progress bar. Valid ranges are from
 +     *            0 to 10000 (both inclusive). If 10000 is given, the progress
 +     *            bar will be completely filled and will fade out.
 +     */
 +    public abstract void setProgress(int progress);
 +
 +    /**
 +     * Sets the secondary progress for the progress bar in the title. This
 +     * progress is drawn between the primary progress (set via
 +     * {@link #setProgress(int)} and the background. It can be ideal for media
 +     * scenarios such as showing the buffering progress while the default
 +     * progress shows the play progress.
 +     * <p>
 +     * In order for the progress bar to be shown, the feature must be requested
 +     * via {@link #requestWindowFeature(int)}.
 +     *
 +     * @param secondaryProgress The secondary progress for the progress bar. Valid ranges are from
 +     *            0 to 10000 (both inclusive).
 +     */
 +    public abstract void setSecondaryProgress(int secondaryProgress);
 +
 +    /**
 +     * Get a menu inflater instance which supports the newer menu attributes.
 +     *
 +     * @return Menu inflater instance.
 +     */
 +    public MenuInflater getMenuInflater() {
 +        if (DEBUG) Log.d(TAG, "[getMenuInflater]");
 +
 +        // Make sure that action views can get an appropriate theme.
 +        if (mMenuInflater == null) {
 +            if (getActionBar() != null) {
 +                mMenuInflater = new MenuInflater(getThemedContext(), mActivity);
 +            } else {
 +                mMenuInflater = new MenuInflater(mActivity);
 +            }
 +        }
 +        return mMenuInflater;
 +    }
 +
 +    protected abstract Context getThemedContext();
 +
 +    /**
 +     * Start an action mode.
 +     *
 +     * @param callback Callback that will manage lifecycle events for this
 +     *                 context mode.
 +     * @return The ContextMode that was started, or null if it was canceled.
 +     * @see ActionMode
 +     */
 +    public abstract ActionMode startActionMode(ActionMode.Callback callback);
 +}
 diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/ActionBar.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/ActionBar.java new file mode 100644 index 000000000..03755be2b --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/ActionBar.java @@ -0,0 +1,956 @@ +/*
 + * Copyright (C) 2010 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.actionbarsherlock.app;
 +
 +import android.content.Context;
 +import android.content.res.TypedArray;
 +import android.graphics.drawable.Drawable;
 +import android.support.v4.app.FragmentTransaction;
 +import android.util.AttributeSet;
 +import android.view.Gravity;
 +import android.view.View;
 +import android.view.ViewDebug;
 +import android.view.ViewGroup;
 +import android.view.ViewGroup.MarginLayoutParams;
 +import android.widget.SpinnerAdapter;
 +
 +/**
 + * A window feature at the top of the activity that may display the activity title, navigation
 + * modes, and other interactive items.
 + * <p>Beginning with Android 3.0 (API level 11), the action bar appears at the top of an
 + * activity's window when the activity uses the system's {@link
 + * android.R.style#Theme_Holo Holo} theme (or one of its descendant themes), which is the default.
 + * You may otherwise add the action bar by calling {@link
 + * android.view.Window#requestFeature requestFeature(FEATURE_ACTION_BAR)} or by declaring it in a
 + * custom theme with the {@link android.R.styleable#Theme_windowActionBar windowActionBar} property.
 + * <p>By default, the action bar shows the application icon on
 + * the left, followed by the activity title. If your activity has an options menu, you can make
 + * select items accessible directly from the action bar as "action items". You can also
 + * modify various characteristics of the action bar or remove it completely.</p>
 + * <p>From your activity, you can retrieve an instance of {@link ActionBar} by calling {@link
 + * android.app.Activity#getActionBar getActionBar()}.</p>
 + * <p>In some cases, the action bar may be overlayed by another bar that enables contextual actions,
 + * using an {@link android.view.ActionMode}. For example, when the user selects one or more items in
 + * your activity, you can enable an action mode that offers actions specific to the selected
 + * items, with a UI that temporarily replaces the action bar. Although the UI may occupy the
 + * same space, the {@link android.view.ActionMode} APIs are distinct and independent from those for
 + * {@link ActionBar}.
 + * <div class="special reference">
 + * <h3>Developer Guides</h3>
 + * <p>For information about how to use the action bar, including how to add action items, navigation
 + * modes and more, read the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action
 + * Bar</a> developer guide.</p>
 + * </div>
 + */
 +public abstract class ActionBar {
 +    /**
 +     * Standard navigation mode. Consists of either a logo or icon
 +     * and title text with an optional subtitle. Clicking any of these elements
 +     * will dispatch onOptionsItemSelected to the host Activity with
 +     * a MenuItem with item ID android.R.id.home.
 +     */
 +    public static final int NAVIGATION_MODE_STANDARD = android.app.ActionBar.NAVIGATION_MODE_STANDARD;
 +
 +    /**
 +     * List navigation mode. Instead of static title text this mode
 +     * presents a list menu for navigation within the activity.
 +     * e.g. this might be presented to the user as a dropdown list.
 +     */
 +    public static final int NAVIGATION_MODE_LIST = android.app.ActionBar.NAVIGATION_MODE_LIST;
 +
 +    /**
 +     * Tab navigation mode. Instead of static title text this mode
 +     * presents a series of tabs for navigation within the activity.
 +     */
 +    public static final int NAVIGATION_MODE_TABS = android.app.ActionBar.NAVIGATION_MODE_TABS;
 +
 +    /**
 +     * Use logo instead of icon if available. This flag will cause appropriate
 +     * navigation modes to use a wider logo in place of the standard icon.
 +     *
 +     * @see #setDisplayOptions(int)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public static final int DISPLAY_USE_LOGO = android.app.ActionBar.DISPLAY_USE_LOGO;
 +
 +    /**
 +     * Show 'home' elements in this action bar, leaving more space for other
 +     * navigation elements. This includes logo and icon.
 +     *
 +     * @see #setDisplayOptions(int)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public static final int DISPLAY_SHOW_HOME = android.app.ActionBar.DISPLAY_SHOW_HOME;
 +
 +    /**
 +     * Display the 'home' element such that it appears as an 'up' affordance.
 +     * e.g. show an arrow to the left indicating the action that will be taken.
 +     *
 +     * Set this flag if selecting the 'home' button in the action bar to return
 +     * up by a single level in your UI rather than back to the top level or front page.
 +     *
 +     * <p>Setting this option will implicitly enable interaction with the home/up
 +     * button. See {@link #setHomeButtonEnabled(boolean)}.
 +     *
 +     * @see #setDisplayOptions(int)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public static final int DISPLAY_HOME_AS_UP = android.app.ActionBar.DISPLAY_HOME_AS_UP;
 +
 +    /**
 +     * Show the activity title and subtitle, if present.
 +     *
 +     * @see #setTitle(CharSequence)
 +     * @see #setTitle(int)
 +     * @see #setSubtitle(CharSequence)
 +     * @see #setSubtitle(int)
 +     * @see #setDisplayOptions(int)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public static final int DISPLAY_SHOW_TITLE = android.app.ActionBar.DISPLAY_SHOW_TITLE;
 +
 +    /**
 +     * Show the custom view if one has been set.
 +     * @see #setCustomView(View)
 +     * @see #setDisplayOptions(int)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public static final int DISPLAY_SHOW_CUSTOM = android.app.ActionBar.DISPLAY_SHOW_CUSTOM;
 +
 +    /**
 +     * Set the action bar into custom navigation mode, supplying a view
 +     * for custom navigation.
 +     *
 +     * Custom navigation views appear between the application icon and
 +     * any action buttons and may use any space available there. Common
 +     * use cases for custom navigation views might include an auto-suggesting
 +     * address bar for a browser or other navigation mechanisms that do not
 +     * translate well to provided navigation modes.
 +     *
 +     * @param view Custom navigation view to place in the ActionBar.
 +     */
 +    public abstract void setCustomView(View view);
 +
 +    /**
 +     * Set the action bar into custom navigation mode, supplying a view
 +     * for custom navigation.
 +     *
 +     * <p>Custom navigation views appear between the application icon and
 +     * any action buttons and may use any space available there. Common
 +     * use cases for custom navigation views might include an auto-suggesting
 +     * address bar for a browser or other navigation mechanisms that do not
 +     * translate well to provided navigation modes.</p>
 +     *
 +     * <p>The display option {@link #DISPLAY_SHOW_CUSTOM} must be set for
 +     * the custom view to be displayed.</p>
 +     *
 +     * @param view Custom navigation view to place in the ActionBar.
 +     * @param layoutParams How this custom view should layout in the bar.
 +     *
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public abstract void setCustomView(View view, LayoutParams layoutParams);
 +
 +    /**
 +     * Set the action bar into custom navigation mode, supplying a view
 +     * for custom navigation.
 +     *
 +     * <p>Custom navigation views appear between the application icon and
 +     * any action buttons and may use any space available there. Common
 +     * use cases for custom navigation views might include an auto-suggesting
 +     * address bar for a browser or other navigation mechanisms that do not
 +     * translate well to provided navigation modes.</p>
 +     *
 +     * <p>The display option {@link #DISPLAY_SHOW_CUSTOM} must be set for
 +     * the custom view to be displayed.</p>
 +     *
 +     * @param resId Resource ID of a layout to inflate into the ActionBar.
 +     *
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public abstract void setCustomView(int resId);
 +
 +    /**
 +     * Set the icon to display in the 'home' section of the action bar.
 +     * The action bar will use an icon specified by its style or the
 +     * activity icon by default.
 +     *
 +     * Whether the home section shows an icon or logo is controlled
 +     * by the display option {@link #DISPLAY_USE_LOGO}.
 +     *
 +     * @param resId Resource ID of a drawable to show as an icon.
 +     *
 +     * @see #setDisplayUseLogoEnabled(boolean)
 +     * @see #setDisplayShowHomeEnabled(boolean)
 +     */
 +    public abstract void setIcon(int resId);
 +
 +    /**
 +     * Set the icon to display in the 'home' section of the action bar.
 +     * The action bar will use an icon specified by its style or the
 +     * activity icon by default.
 +     *
 +     * Whether the home section shows an icon or logo is controlled
 +     * by the display option {@link #DISPLAY_USE_LOGO}.
 +     *
 +     * @param icon Drawable to show as an icon.
 +     *
 +     * @see #setDisplayUseLogoEnabled(boolean)
 +     * @see #setDisplayShowHomeEnabled(boolean)
 +     */
 +    public abstract void setIcon(Drawable icon);
 +
 +    /**
 +     * Set the logo to display in the 'home' section of the action bar.
 +     * The action bar will use a logo specified by its style or the
 +     * activity logo by default.
 +     *
 +     * Whether the home section shows an icon or logo is controlled
 +     * by the display option {@link #DISPLAY_USE_LOGO}.
 +     *
 +     * @param resId Resource ID of a drawable to show as a logo.
 +     *
 +     * @see #setDisplayUseLogoEnabled(boolean)
 +     * @see #setDisplayShowHomeEnabled(boolean)
 +     */
 +    public abstract void setLogo(int resId);
 +
 +    /**
 +     * Set the logo to display in the 'home' section of the action bar.
 +     * The action bar will use a logo specified by its style or the
 +     * activity logo by default.
 +     *
 +     * Whether the home section shows an icon or logo is controlled
 +     * by the display option {@link #DISPLAY_USE_LOGO}.
 +     *
 +     * @param logo Drawable to show as a logo.
 +     *
 +     * @see #setDisplayUseLogoEnabled(boolean)
 +     * @see #setDisplayShowHomeEnabled(boolean)
 +     */
 +    public abstract void setLogo(Drawable logo);
 +
 +    /**
 +     * Set the adapter and navigation callback for list navigation mode.
 +     *
 +     * The supplied adapter will provide views for the expanded list as well as
 +     * the currently selected item. (These may be displayed differently.)
 +     *
 +     * The supplied OnNavigationListener will alert the application when the user
 +     * changes the current list selection.
 +     *
 +     * @param adapter An adapter that will provide views both to display
 +     *                the current navigation selection and populate views
 +     *                within the dropdown navigation menu.
 +     * @param callback An OnNavigationListener that will receive events when the user
 +     *                 selects a navigation item.
 +     */
 +    public abstract void setListNavigationCallbacks(SpinnerAdapter adapter,
 +            OnNavigationListener callback);
 +
 +    /**
 +     * Set the selected navigation item in list or tabbed navigation modes.
 +     *
 +     * @param position Position of the item to select.
 +     */
 +    public abstract void setSelectedNavigationItem(int position);
 +
 +    /**
 +     * Get the position of the selected navigation item in list or tabbed navigation modes.
 +     *
 +     * @return Position of the selected item.
 +     */
 +    public abstract int getSelectedNavigationIndex();
 +
 +    /**
 +     * Get the number of navigation items present in the current navigation mode.
 +     *
 +     * @return Number of navigation items.
 +     */
 +    public abstract int getNavigationItemCount();
 +
 +    /**
 +     * Set the action bar's title. This will only be displayed if
 +     * {@link #DISPLAY_SHOW_TITLE} is set.
 +     *
 +     * @param title Title to set
 +     *
 +     * @see #setTitle(int)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public abstract void setTitle(CharSequence title);
 +
 +    /**
 +     * Set the action bar's title. This will only be displayed if
 +     * {@link #DISPLAY_SHOW_TITLE} is set.
 +     *
 +     * @param resId Resource ID of title string to set
 +     *
 +     * @see #setTitle(CharSequence)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public abstract void setTitle(int resId);
 +
 +    /**
 +     * Set the action bar's subtitle. This will only be displayed if
 +     * {@link #DISPLAY_SHOW_TITLE} is set. Set to null to disable the
 +     * subtitle entirely.
 +     *
 +     * @param subtitle Subtitle to set
 +     *
 +     * @see #setSubtitle(int)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public abstract void setSubtitle(CharSequence subtitle);
 +
 +    /**
 +     * Set the action bar's subtitle. This will only be displayed if
 +     * {@link #DISPLAY_SHOW_TITLE} is set.
 +     *
 +     * @param resId Resource ID of subtitle string to set
 +     *
 +     * @see #setSubtitle(CharSequence)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public abstract void setSubtitle(int resId);
 +
 +    /**
 +     * Set display options. This changes all display option bits at once. To change
 +     * a limited subset of display options, see {@link #setDisplayOptions(int, int)}.
 +     *
 +     * @param options A combination of the bits defined by the DISPLAY_ constants
 +     *                defined in ActionBar.
 +     */
 +    public abstract void setDisplayOptions(int options);
 +
 +    /**
 +     * Set selected display options. Only the options specified by mask will be changed.
 +     * To change all display option bits at once, see {@link #setDisplayOptions(int)}.
 +     *
 +     * <p>Example: setDisplayOptions(0, DISPLAY_SHOW_HOME) will disable the
 +     * {@link #DISPLAY_SHOW_HOME} option.
 +     * setDisplayOptions(DISPLAY_SHOW_HOME, DISPLAY_SHOW_HOME | DISPLAY_USE_LOGO)
 +     * will enable {@link #DISPLAY_SHOW_HOME} and disable {@link #DISPLAY_USE_LOGO}.
 +     *
 +     * @param options A combination of the bits defined by the DISPLAY_ constants
 +     *                defined in ActionBar.
 +     * @param mask A bit mask declaring which display options should be changed.
 +     */
 +    public abstract void setDisplayOptions(int options, int mask);
 +
 +    /**
 +     * Set whether to display the activity logo rather than the activity icon.
 +     * A logo is often a wider, more detailed image.
 +     *
 +     * <p>To set several display options at once, see the setDisplayOptions methods.
 +     *
 +     * @param useLogo true to use the activity logo, false to use the activity icon.
 +     *
 +     * @see #setDisplayOptions(int)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public abstract void setDisplayUseLogoEnabled(boolean useLogo);
 +
 +    /**
 +     * Set whether to include the application home affordance in the action bar.
 +     * Home is presented as either an activity icon or logo.
 +     *
 +     * <p>To set several display options at once, see the setDisplayOptions methods.
 +     *
 +     * @param showHome true to show home, false otherwise.
 +     *
 +     * @see #setDisplayOptions(int)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public abstract void setDisplayShowHomeEnabled(boolean showHome);
 +
 +    /**
 +     * Set whether home should be displayed as an "up" affordance.
 +     * Set this to true if selecting "home" returns up by a single level in your UI
 +     * rather than back to the top level or front page.
 +     *
 +     * <p>To set several display options at once, see the setDisplayOptions methods.
 +     *
 +     * @param showHomeAsUp true to show the user that selecting home will return one
 +     *                     level up rather than to the top level of the app.
 +     *
 +     * @see #setDisplayOptions(int)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public abstract void setDisplayHomeAsUpEnabled(boolean showHomeAsUp);
 +
 +    /**
 +     * Set whether an activity title/subtitle should be displayed.
 +     *
 +     * <p>To set several display options at once, see the setDisplayOptions methods.
 +     *
 +     * @param showTitle true to display a title/subtitle if present.
 +     *
 +     * @see #setDisplayOptions(int)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public abstract void setDisplayShowTitleEnabled(boolean showTitle);
 +
 +    /**
 +     * Set whether a custom view should be displayed, if set.
 +     *
 +     * <p>To set several display options at once, see the setDisplayOptions methods.
 +     *
 +     * @param showCustom true if the currently set custom view should be displayed, false otherwise.
 +     *
 +     * @see #setDisplayOptions(int)
 +     * @see #setDisplayOptions(int, int)
 +     */
 +    public abstract void setDisplayShowCustomEnabled(boolean showCustom);
 +
 +    /**
 +     * Set the ActionBar's background. This will be used for the primary
 +     * action bar.
 +     *
 +     * @param d Background drawable
 +     * @see #setStackedBackgroundDrawable(Drawable)
 +     * @see #setSplitBackgroundDrawable(Drawable)
 +     */
 +    public abstract void setBackgroundDrawable(Drawable d);
 +
 +    /**
 +     * Set the ActionBar's stacked background. This will appear
 +     * in the second row/stacked bar on some devices and configurations.
 +     *
 +     * @param d Background drawable for the stacked row
 +     */
 +    public void setStackedBackgroundDrawable(Drawable d) { }
 +
 +    /**
 +     * Set the ActionBar's split background. This will appear in
 +     * the split action bar containing menu-provided action buttons
 +     * on some devices and configurations.
 +     * <p>You can enable split action bar with {@link android.R.attr#uiOptions}
 +     *
 +     * @param d Background drawable for the split bar
 +     */
 +    public void setSplitBackgroundDrawable(Drawable d) { }
 +
 +    /**
 +     * @return The current custom view.
 +     */
 +    public abstract View getCustomView();
 +
 +    /**
 +     * Returns the current ActionBar title in standard mode.
 +     * Returns null if {@link #getNavigationMode()} would not return
 +     * {@link #NAVIGATION_MODE_STANDARD}.
 +     *
 +     * @return The current ActionBar title or null.
 +     */
 +    public abstract CharSequence getTitle();
 +
 +    /**
 +     * Returns the current ActionBar subtitle in standard mode.
 +     * Returns null if {@link #getNavigationMode()} would not return
 +     * {@link #NAVIGATION_MODE_STANDARD}.
 +     *
 +     * @return The current ActionBar subtitle or null.
 +     */
 +    public abstract CharSequence getSubtitle();
 +
 +    /**
 +     * Returns the current navigation mode. The result will be one of:
 +     * <ul>
 +     * <li>{@link #NAVIGATION_MODE_STANDARD}</li>
 +     * <li>{@link #NAVIGATION_MODE_LIST}</li>
 +     * <li>{@link #NAVIGATION_MODE_TABS}</li>
 +     * </ul>
 +     *
 +     * @return The current navigation mode.
 +     */
 +    public abstract int getNavigationMode();
 +
 +    /**
 +     * Set the current navigation mode.
 +     *
 +     * @param mode The new mode to set.
 +     * @see #NAVIGATION_MODE_STANDARD
 +     * @see #NAVIGATION_MODE_LIST
 +     * @see #NAVIGATION_MODE_TABS
 +     */
 +    public abstract void setNavigationMode(int mode);
 +
 +    /**
 +     * @return The current set of display options.
 +     */
 +    public abstract int getDisplayOptions();
 +
 +    /**
 +     * Create and return a new {@link Tab}.
 +     * This tab will not be included in the action bar until it is added.
 +     *
 +     * <p>Very often tabs will be used to switch between {@link Fragment}
 +     * objects.  Here is a typical implementation of such tabs:</p>
 +     *
 +     * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabs.java
 +     *      complete}
 +     *
 +     * @return A new Tab
 +     *
 +     * @see #addTab(Tab)
 +     */
 +    public abstract Tab newTab();
 +
 +    /**
 +     * Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list.
 +     * If this is the first tab to be added it will become the selected tab.
 +     *
 +     * @param tab Tab to add
 +     */
 +    public abstract void addTab(Tab tab);
 +
 +    /**
 +     * Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list.
 +     *
 +     * @param tab Tab to add
 +     * @param setSelected True if the added tab should become the selected tab.
 +     */
 +    public abstract void addTab(Tab tab, boolean setSelected);
 +
 +    /**
 +     * Add a tab for use in tabbed navigation mode. The tab will be inserted at
 +     * <code>position</code>. If this is the first tab to be added it will become
 +     * the selected tab.
 +     *
 +     * @param tab The tab to add
 +     * @param position The new position of the tab
 +     */
 +    public abstract void addTab(Tab tab, int position);
 +
 +    /**
 +     * Add a tab for use in tabbed navigation mode. The tab will be insterted at
 +     * <code>position</code>.
 +     *
 +     * @param tab The tab to add
 +     * @param position The new position of the tab
 +     * @param setSelected True if the added tab should become the selected tab.
 +     */
 +    public abstract void addTab(Tab tab, int position, boolean setSelected);
 +
 +    /**
 +     * Remove a tab from the action bar. If the removed tab was selected it will be deselected
 +     * and another tab will be selected if present.
 +     *
 +     * @param tab The tab to remove
 +     */
 +    public abstract void removeTab(Tab tab);
 +
 +    /**
 +     * Remove a tab from the action bar. If the removed tab was selected it will be deselected
 +     * and another tab will be selected if present.
 +     *
 +     * @param position Position of the tab to remove
 +     */
 +    public abstract void removeTabAt(int position);
 +
 +    /**
 +     * Remove all tabs from the action bar and deselect the current tab.
 +     */
 +    public abstract void removeAllTabs();
 +
 +    /**
 +     * Select the specified tab. If it is not a child of this action bar it will be added.
 +     *
 +     * <p>Note: If you want to select by index, use {@link #setSelectedNavigationItem(int)}.</p>
 +     *
 +     * @param tab Tab to select
 +     */
 +    public abstract void selectTab(Tab tab);
 +
 +    /**
 +     * Returns the currently selected tab if in tabbed navigation mode and there is at least
 +     * one tab present.
 +     *
 +     * @return The currently selected tab or null
 +     */
 +    public abstract Tab getSelectedTab();
 +
 +    /**
 +     * Returns the tab at the specified index.
 +     *
 +     * @param index Index value in the range 0-get
 +     * @return
 +     */
 +    public abstract Tab getTabAt(int index);
 +
 +    /**
 +     * Returns the number of tabs currently registered with the action bar.
 +     * @return Tab count
 +     */
 +    public abstract int getTabCount();
 +
 +    /**
 +     * Retrieve the current height of the ActionBar.
 +     *
 +     * @return The ActionBar's height
 +     */
 +    public abstract int getHeight();
 +
 +    /**
 +     * Show the ActionBar if it is not currently showing.
 +     * If the window hosting the ActionBar does not have the feature
 +     * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
 +     * content to fit the new space available.
 +     */
 +    public abstract void show();
 +
 +    /**
 +     * Hide the ActionBar if it is currently showing.
 +     * If the window hosting the ActionBar does not have the feature
 +     * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
 +     * content to fit the new space available.
 +     */
 +    public abstract void hide();
 +
 +    /**
 +     * @return <code>true</code> if the ActionBar is showing, <code>false</code> otherwise.
 +     */
 +    public abstract boolean isShowing();
 +
 +    /**
 +     * Add a listener that will respond to menu visibility change events.
 +     *
 +     * @param listener The new listener to add
 +     */
 +    public abstract void addOnMenuVisibilityListener(OnMenuVisibilityListener listener);
 +
 +    /**
 +     * Remove a menu visibility listener. This listener will no longer receive menu
 +     * visibility change events.
 +     *
 +     * @param listener A listener to remove that was previously added
 +     */
 +    public abstract void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener);
 +
 +    /**
 +     * Enable or disable the "home" button in the corner of the action bar. (Note that this
 +     * is the application home/up affordance on the action bar, not the systemwide home
 +     * button.)
 +     *
 +     * <p>This defaults to true for packages targeting < API 14. For packages targeting
 +     * API 14 or greater, the application should call this method to enable interaction
 +     * with the home/up affordance.
 +     *
 +     * <p>Setting the {@link #DISPLAY_HOME_AS_UP} display option will automatically enable
 +     * the home button.
 +     *
 +     * @param enabled true to enable the home button, false to disable the home button.
 +     */
 +    public void setHomeButtonEnabled(boolean enabled) { }
 +
 +    /**
 +     * Returns a {@link Context} with an appropriate theme for creating views that
 +     * will appear in the action bar. If you are inflating or instantiating custom views
 +     * that will appear in an action bar, you should use the Context returned by this method.
 +     * (This includes adapters used for list navigation mode.)
 +     * This will ensure that views contrast properly against the action bar.
 +     *
 +     * @return A themed Context for creating views
 +     */
 +    public Context getThemedContext() { return null; }
 +
 +    /**
 +     * Listener interface for ActionBar navigation events.
 +     */
 +    public interface OnNavigationListener {
 +        /**
 +         * This method is called whenever a navigation item in your action bar
 +         * is selected.
 +         *
 +         * @param itemPosition Position of the item clicked.
 +         * @param itemId ID of the item clicked.
 +         * @return True if the event was handled, false otherwise.
 +         */
 +        public boolean onNavigationItemSelected(int itemPosition, long itemId);
 +    }
 +
 +    /**
 +     * Listener for receiving events when action bar menus are shown or hidden.
 +     */
 +    public interface OnMenuVisibilityListener {
 +        /**
 +         * Called when an action bar menu is shown or hidden. Applications may want to use
 +         * this to tune auto-hiding behavior for the action bar or pause/resume video playback,
 +         * gameplay, or other activity within the main content area.
 +         *
 +         * @param isVisible True if an action bar menu is now visible, false if no action bar
 +         *                  menus are visible.
 +         */
 +        public void onMenuVisibilityChanged(boolean isVisible);
 +    }
 +
 +    /**
 +     * A tab in the action bar.
 +     *
 +     * <p>Tabs manage the hiding and showing of {@link Fragment}s.
 +     */
 +    public static abstract class Tab {
 +        /**
 +         * An invalid position for a tab.
 +         *
 +         * @see #getPosition()
 +         */
 +        public static final int INVALID_POSITION = -1;
 +
 +        /**
 +         * Return the current position of this tab in the action bar.
 +         *
 +         * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in
 +         *         the action bar.
 +         */
 +        public abstract int getPosition();
 +
 +        /**
 +         * Return the icon associated with this tab.
 +         *
 +         * @return The tab's icon
 +         */
 +        public abstract Drawable getIcon();
 +
 +        /**
 +         * Return the text of this tab.
 +         *
 +         * @return The tab's text
 +         */
 +        public abstract CharSequence getText();
 +
 +        /**
 +         * Set the icon displayed on this tab.
 +         *
 +         * @param icon The drawable to use as an icon
 +         * @return The current instance for call chaining
 +         */
 +        public abstract Tab setIcon(Drawable icon);
 +
 +        /**
 +         * Set the icon displayed on this tab.
 +         *
 +         * @param resId Resource ID referring to the drawable to use as an icon
 +         * @return The current instance for call chaining
 +         */
 +        public abstract Tab setIcon(int resId);
 +
 +        /**
 +         * Set the text displayed on this tab. Text may be truncated if there is not
 +         * room to display the entire string.
 +         *
 +         * @param text The text to display
 +         * @return The current instance for call chaining
 +         */
 +        public abstract Tab setText(CharSequence text);
 +
 +        /**
 +         * Set the text displayed on this tab. Text may be truncated if there is not
 +         * room to display the entire string.
 +         *
 +         * @param resId A resource ID referring to the text that should be displayed
 +         * @return The current instance for call chaining
 +         */
 +        public abstract Tab setText(int resId);
 +
 +        /**
 +         * Set a custom view to be used for this tab. This overrides values set by
 +         * {@link #setText(CharSequence)} and {@link #setIcon(Drawable)}.
 +         *
 +         * @param view Custom view to be used as a tab.
 +         * @return The current instance for call chaining
 +         */
 +        public abstract Tab setCustomView(View view);
 +
 +        /**
 +         * Set a custom view to be used for this tab. This overrides values set by
 +         * {@link #setText(CharSequence)} and {@link #setIcon(Drawable)}.
 +         *
 +         * @param layoutResId A layout resource to inflate and use as a custom tab view
 +         * @return The current instance for call chaining
 +         */
 +        public abstract Tab setCustomView(int layoutResId);
 +
 +        /**
 +         * Retrieve a previously set custom view for this tab.
 +         *
 +         * @return The custom view set by {@link #setCustomView(View)}.
 +         */
 +        public abstract View getCustomView();
 +
 +        /**
 +         * Give this Tab an arbitrary object to hold for later use.
 +         *
 +         * @param obj Object to store
 +         * @return The current instance for call chaining
 +         */
 +        public abstract Tab setTag(Object obj);
 +
 +        /**
 +         * @return This Tab's tag object.
 +         */
 +        public abstract Object getTag();
 +
 +        /**
 +         * Set the {@link TabListener} that will handle switching to and from this tab.
 +         * All tabs must have a TabListener set before being added to the ActionBar.
 +         *
 +         * @param listener Listener to handle tab selection events
 +         * @return The current instance for call chaining
 +         */
 +        public abstract Tab setTabListener(TabListener listener);
 +
 +        /**
 +         * Select this tab. Only valid if the tab has been added to the action bar.
 +         */
 +        public abstract void select();
 +
 +        /**
 +         * Set a description of this tab's content for use in accessibility support.
 +         * If no content description is provided the title will be used.
 +         *
 +         * @param resId A resource ID referring to the description text
 +         * @return The current instance for call chaining
 +         * @see #setContentDescription(CharSequence)
 +         * @see #getContentDescription()
 +         */
 +        public abstract Tab setContentDescription(int resId);
 +
 +        /**
 +         * Set a description of this tab's content for use in accessibility support.
 +         * If no content description is provided the title will be used.
 +         *
 +         * @param contentDesc Description of this tab's content
 +         * @return The current instance for call chaining
 +         * @see #setContentDescription(int)
 +         * @see #getContentDescription()
 +         */
 +        public abstract Tab setContentDescription(CharSequence contentDesc);
 +
 +        /**
 +         * Gets a brief description of this tab's content for use in accessibility support.
 +         *
 +         * @return Description of this tab's content
 +         * @see #setContentDescription(CharSequence)
 +         * @see #setContentDescription(int)
 +         */
 +        public abstract CharSequence getContentDescription();
 +    }
 +
 +    /**
 +     * Callback interface invoked when a tab is focused, unfocused, added, or removed.
 +     */
 +    public interface TabListener {
 +        /**
 +         * Called when a tab enters the selected state.
 +         *
 +         * @param tab The tab that was selected
 +         * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
 +         *        during a tab switch. The previous tab's unselect and this tab's select will be
 +         *        executed in a single transaction. This FragmentTransaction does not support
 +         *        being added to the back stack.
 +         */
 +        public void onTabSelected(Tab tab, FragmentTransaction ft);
 +
 +        /**
 +         * Called when a tab exits the selected state.
 +         *
 +         * @param tab The tab that was unselected
 +         * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
 +         *        during a tab switch. This tab's unselect and the newly selected tab's select
 +         *        will be executed in a single transaction. This FragmentTransaction does not
 +         *        support being added to the back stack.
 +         */
 +        public void onTabUnselected(Tab tab, FragmentTransaction ft);
 +
 +        /**
 +         * Called when a tab that is already selected is chosen again by the user.
 +         * Some applications may use this action to return to the top level of a category.
 +         *
 +         * @param tab The tab that was reselected.
 +         * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
 +         *        once this method returns. This FragmentTransaction does not support
 +         *        being added to the back stack.
 +         */
 +        public void onTabReselected(Tab tab, FragmentTransaction ft);
 +    }
 +
 +    /**
 +     * Per-child layout information associated with action bar custom views.
 +     *
 +     * @attr ref android.R.styleable#ActionBar_LayoutParams_layout_gravity
 +     */
 +    public static class LayoutParams extends MarginLayoutParams {
 +        private static final int[] ATTRS = new int[] {
 +                android.R.attr.layout_gravity
 +        };
 +
 +        /**
 +         * Gravity for the view associated with these LayoutParams.
 +         *
 +         * @see android.view.Gravity
 +         */
 +        @ViewDebug.ExportedProperty(mapping = {
 +            @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
 +            @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
 +            @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
 +            @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
 +            @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
 +            @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
 +            @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
 +            @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
 +            @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
 +            @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL,   to = "FILL_HORIZONTAL"),
 +            @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
 +            @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
 +        })
 +        public int gravity = -1;
 +
 +        public LayoutParams(Context c, AttributeSet attrs) {
 +            super(c, attrs);
 +
 +            TypedArray a = c.obtainStyledAttributes(attrs, ATTRS);
 +            gravity = a.getInt(0, -1);
 +            a.recycle();
 +        }
 +
 +        public LayoutParams(int width, int height) {
 +            super(width, height);
 +            this.gravity = Gravity.CENTER_VERTICAL | Gravity.LEFT;
 +        }
 +
 +        public LayoutParams(int width, int height, int gravity) {
 +            super(width, height);
 +            this.gravity = gravity;
 +        }
 +
 +        public LayoutParams(int gravity) {
 +            this(WRAP_CONTENT, FILL_PARENT, gravity);
 +        }
 +
 +        public LayoutParams(LayoutParams source) {
 +            super(source);
 +
 +            this.gravity = source.gravity;
 +        }
 +
 +        public LayoutParams(ViewGroup.LayoutParams source) {
 +            super(source);
 +        }
 +    }
 +}
 diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockActivity.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockActivity.java new file mode 100644 index 000000000..7b4543640 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockActivity.java @@ -0,0 +1,270 @@ +package com.actionbarsherlock.app;
 +
 +import android.app.Activity;
 +import android.content.res.Configuration;
 +import android.os.Bundle;
 +import android.view.KeyEvent;
 +import android.view.View;
 +import android.view.Window;
 +import android.view.ViewGroup.LayoutParams;
 +import com.actionbarsherlock.ActionBarSherlock;
 +import com.actionbarsherlock.ActionBarSherlock.OnActionModeFinishedListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnActionModeStartedListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnCreatePanelMenuListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnMenuItemSelectedListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnPreparePanelListener;
 +import com.actionbarsherlock.view.ActionMode;
 +import com.actionbarsherlock.view.Menu;
 +import com.actionbarsherlock.view.MenuInflater;
 +import com.actionbarsherlock.view.MenuItem;
 +
 +public abstract class SherlockActivity extends Activity implements OnCreatePanelMenuListener, OnPreparePanelListener, OnMenuItemSelectedListener, OnActionModeStartedListener, OnActionModeFinishedListener {
 +    private ActionBarSherlock mSherlock;
 +
 +    protected final ActionBarSherlock getSherlock() {
 +        if (mSherlock == null) {
 +            mSherlock = ActionBarSherlock.wrap(this, ActionBarSherlock.FLAG_DELEGATE);
 +        }
 +        return mSherlock;
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Action bar and mode
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    public ActionBar getSupportActionBar() {
 +        return getSherlock().getActionBar();
 +    }
 +
 +    public ActionMode startActionMode(ActionMode.Callback callback) {
 +        return getSherlock().startActionMode(callback);
 +    }
 +
 +    @Override
 +    public void onActionModeStarted(ActionMode mode) {}
 +
 +    @Override
 +    public void onActionModeFinished(ActionMode mode) {}
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // General lifecycle/callback dispatching
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    @Override
 +    public void onConfigurationChanged(Configuration newConfig) {
 +        super.onConfigurationChanged(newConfig);
 +        getSherlock().dispatchConfigurationChanged(newConfig);
 +    }
 +
 +    @Override
 +    protected void onPostResume() {
 +        super.onPostResume();
 +        getSherlock().dispatchPostResume();
 +    }
 +
 +    @Override
 +    protected void onPause() {
 +        getSherlock().dispatchPause();
 +        super.onPause();
 +    }
 +
 +    @Override
 +    protected void onStop() {
 +        getSherlock().dispatchStop();
 +        super.onStop();
 +    }
 +
 +    @Override
 +    protected void onDestroy() {
 +        getSherlock().dispatchDestroy();
 +        super.onDestroy();
 +    }
 +
 +    @Override
 +    protected void onPostCreate(Bundle savedInstanceState) {
 +        getSherlock().dispatchPostCreate(savedInstanceState);
 +        super.onPostCreate(savedInstanceState);
 +    }
 +
 +    @Override
 +    protected void onTitleChanged(CharSequence title, int color) {
 +        getSherlock().dispatchTitleChanged(title, color);
 +        super.onTitleChanged(title, color);
 +    }
 +
 +    @Override
 +    public final boolean onMenuOpened(int featureId, android.view.Menu menu) {
 +        if (getSherlock().dispatchMenuOpened(featureId, menu)) {
 +            return true;
 +        }
 +        return super.onMenuOpened(featureId, menu);
 +    }
 +
 +    @Override
 +    public void onPanelClosed(int featureId, android.view.Menu menu) {
 +        getSherlock().dispatchPanelClosed(featureId, menu);
 +        super.onPanelClosed(featureId, menu);
 +    }
 +
 +    @Override
 +    public boolean dispatchKeyEvent(KeyEvent event) {
 +        if (getSherlock().dispatchKeyEvent(event)) {
 +            return true;
 +        }
 +        return super.dispatchKeyEvent(event);
 +    }
 +
 +    @Override
 +    protected void onSaveInstanceState(Bundle outState) {
 +        super.onSaveInstanceState(outState);
 +        getSherlock().dispatchSaveInstanceState(outState);
 +    }
 +
 +    @Override
 +    protected void onRestoreInstanceState(Bundle savedInstanceState) {
 +        super.onRestoreInstanceState(savedInstanceState);
 +        getSherlock().dispatchRestoreInstanceState(savedInstanceState);
 +    }
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Native menu handling
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    public MenuInflater getSupportMenuInflater() {
 +        return getSherlock().getMenuInflater();
 +    }
 +
 +    public void invalidateOptionsMenu() {
 +        getSherlock().dispatchInvalidateOptionsMenu();
 +    }
 +
 +    public void supportInvalidateOptionsMenu() {
 +        invalidateOptionsMenu();
 +    }
 +
 +    @Override
 +    public final boolean onCreateOptionsMenu(android.view.Menu menu) {
 +        return getSherlock().dispatchCreateOptionsMenu(menu);
 +    }
 +
 +    @Override
 +    public final boolean onPrepareOptionsMenu(android.view.Menu menu) {
 +        return getSherlock().dispatchPrepareOptionsMenu(menu);
 +    }
 +
 +    @Override
 +    public final boolean onOptionsItemSelected(android.view.MenuItem item) {
 +        return getSherlock().dispatchOptionsItemSelected(item);
 +    }
 +
 +    @Override
 +    public void openOptionsMenu() {
 +        if (!getSherlock().dispatchOpenOptionsMenu()) {
 +            super.openOptionsMenu();
 +        }
 +    }
 +
 +    @Override
 +    public void closeOptionsMenu() {
 +        if (!getSherlock().dispatchCloseOptionsMenu()) {
 +            super.closeOptionsMenu();
 +        }
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Sherlock menu handling
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    @Override
 +    public boolean onCreatePanelMenu(int featureId, Menu menu) {
 +        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
 +            return onCreateOptionsMenu(menu);
 +        }
 +        return false;
 +    }
 +
 +    public boolean onCreateOptionsMenu(Menu menu) {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean onPreparePanel(int featureId, View view, Menu menu) {
 +        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
 +            return onPrepareOptionsMenu(menu);
 +        }
 +        return false;
 +    }
 +
 +    public boolean onPrepareOptionsMenu(Menu menu) {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean onMenuItemSelected(int featureId, MenuItem item) {
 +        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
 +            return onOptionsItemSelected(item);
 +        }
 +        return false;
 +    }
 +
 +    public boolean onOptionsItemSelected(MenuItem item) {
 +        return false;
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Content
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    @Override
 +    public void addContentView(View view, LayoutParams params) {
 +        getSherlock().addContentView(view, params);
 +    }
 +
 +    @Override
 +    public void setContentView(int layoutResId) {
 +        getSherlock().setContentView(layoutResId);
 +    }
 +
 +    @Override
 +    public void setContentView(View view, LayoutParams params) {
 +        getSherlock().setContentView(view, params);
 +    }
 +
 +    @Override
 +    public void setContentView(View view) {
 +        getSherlock().setContentView(view);
 +    }
 +
 +    public void requestWindowFeature(long featureId) {
 +        getSherlock().requestFeature((int)featureId);
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Progress Indication
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    public void setSupportProgress(int progress) {
 +        getSherlock().setProgress(progress);
 +    }
 +
 +    public void setSupportProgressBarIndeterminate(boolean indeterminate) {
 +        getSherlock().setProgressBarIndeterminate(indeterminate);
 +    }
 +
 +    public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
 +        getSherlock().setProgressBarIndeterminateVisibility(visible);
 +    }
 +
 +    public void setSupportProgressBarVisibility(boolean visible) {
 +        getSherlock().setProgressBarVisibility(visible);
 +    }
 +
 +    public void setSupportSecondaryProgress(int secondaryProgress) {
 +        getSherlock().setSecondaryProgress(secondaryProgress);
 +    }
 +}
 diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockDialogFragment.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockDialogFragment.java new file mode 100644 index 000000000..a7c856bf0 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockDialogFragment.java @@ -0,0 +1,68 @@ +package com.actionbarsherlock.app; + +import android.app.Activity; +import android.support.v4.app.DialogFragment; +import com.actionbarsherlock.internal.view.menu.MenuItemWrapper; +import com.actionbarsherlock.internal.view.menu.MenuWrapper; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuInflater; +import com.actionbarsherlock.view.MenuItem; + +import static com.actionbarsherlock.app.SherlockFragmentActivity.OnCreateOptionsMenuListener; +import static com.actionbarsherlock.app.SherlockFragmentActivity.OnOptionsItemSelectedListener; +import static com.actionbarsherlock.app.SherlockFragmentActivity.OnPrepareOptionsMenuListener; + +public class SherlockDialogFragment extends DialogFragment implements OnCreateOptionsMenuListener, OnPrepareOptionsMenuListener, OnOptionsItemSelectedListener { +    private SherlockFragmentActivity mActivity; + +    public SherlockFragmentActivity getSherlockActivity() { +        return mActivity; +    } + +    @Override +    public void onAttach(Activity activity) { +        if (!(activity instanceof SherlockFragmentActivity)) { +            throw new IllegalStateException(getClass().getSimpleName() + " must be attached to a SherlockFragmentActivity."); +        } +        mActivity = (SherlockFragmentActivity)activity; + +        super.onAttach(activity); +    } + +    @Override +    public void onDetach() { +        mActivity = null; +        super.onDetach(); +    } + +    @Override +    public final void onCreateOptionsMenu(android.view.Menu menu, android.view.MenuInflater inflater) { +        onCreateOptionsMenu(new MenuWrapper(menu), mActivity.getSupportMenuInflater()); +    } + +    @Override +    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { +        //Nothing to see here. +    } + +    @Override +    public final void onPrepareOptionsMenu(android.view.Menu menu) { +        onPrepareOptionsMenu(new MenuWrapper(menu)); +    } + +    @Override +    public void onPrepareOptionsMenu(Menu menu) { +        //Nothing to see here. +    } + +    @Override +    public final boolean onOptionsItemSelected(android.view.MenuItem item) { +        return onOptionsItemSelected(new MenuItemWrapper(item)); +    } + +    @Override +    public boolean onOptionsItemSelected(MenuItem item) { +        //Nothing to see here. +        return false; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockExpandableListActivity.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockExpandableListActivity.java new file mode 100644 index 000000000..078f9b0ca --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockExpandableListActivity.java @@ -0,0 +1,259 @@ +package com.actionbarsherlock.app;
 +
 +import android.app.ExpandableListActivity;
 +import android.content.res.Configuration;
 +import android.os.Bundle;
 +import android.view.KeyEvent;
 +import android.view.View;
 +import android.view.ViewGroup.LayoutParams;
 +import android.view.Window;
 +import com.actionbarsherlock.ActionBarSherlock;
 +import com.actionbarsherlock.ActionBarSherlock.OnActionModeFinishedListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnActionModeStartedListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnCreatePanelMenuListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnMenuItemSelectedListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnPreparePanelListener;
 +import com.actionbarsherlock.view.ActionMode;
 +import com.actionbarsherlock.view.Menu;
 +import com.actionbarsherlock.view.MenuInflater;
 +import com.actionbarsherlock.view.MenuItem;
 +
 +public abstract class SherlockExpandableListActivity extends ExpandableListActivity implements OnCreatePanelMenuListener, OnPreparePanelListener, OnMenuItemSelectedListener, OnActionModeStartedListener, OnActionModeFinishedListener {
 +    private ActionBarSherlock mSherlock;
 +
 +    protected final ActionBarSherlock getSherlock() {
 +        if (mSherlock == null) {
 +            mSherlock = ActionBarSherlock.wrap(this, ActionBarSherlock.FLAG_DELEGATE);
 +        }
 +        return mSherlock;
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Action bar and mode
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    public ActionBar getSupportActionBar() {
 +        return getSherlock().getActionBar();
 +    }
 +
 +    public ActionMode startActionMode(ActionMode.Callback callback) {
 +        return getSherlock().startActionMode(callback);
 +    }
 +
 +    @Override
 +    public void onActionModeStarted(ActionMode mode) {}
 +
 +    @Override
 +    public void onActionModeFinished(ActionMode mode) {}
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // General lifecycle/callback dispatching
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    @Override
 +    public void onConfigurationChanged(Configuration newConfig) {
 +        super.onConfigurationChanged(newConfig);
 +        getSherlock().dispatchConfigurationChanged(newConfig);
 +    }
 +
 +    @Override
 +    protected void onPostResume() {
 +        super.onPostResume();
 +        getSherlock().dispatchPostResume();
 +    }
 +
 +    @Override
 +    protected void onPause() {
 +        getSherlock().dispatchPause();
 +        super.onPause();
 +    }
 +
 +    @Override
 +    protected void onStop() {
 +        getSherlock().dispatchStop();
 +        super.onStop();
 +    }
 +
 +    @Override
 +    protected void onDestroy() {
 +        getSherlock().dispatchDestroy();
 +        super.onDestroy();
 +    }
 +
 +    @Override
 +    protected void onPostCreate(Bundle savedInstanceState) {
 +        getSherlock().dispatchPostCreate(savedInstanceState);
 +        super.onPostCreate(savedInstanceState);
 +    }
 +
 +    @Override
 +    protected void onTitleChanged(CharSequence title, int color) {
 +        getSherlock().dispatchTitleChanged(title, color);
 +        super.onTitleChanged(title, color);
 +    }
 +
 +    @Override
 +    public final boolean onMenuOpened(int featureId, android.view.Menu menu) {
 +        if (getSherlock().dispatchMenuOpened(featureId, menu)) {
 +            return true;
 +        }
 +        return super.onMenuOpened(featureId, menu);
 +    }
 +
 +    @Override
 +    public void onPanelClosed(int featureId, android.view.Menu menu) {
 +        getSherlock().dispatchPanelClosed(featureId, menu);
 +        super.onPanelClosed(featureId, menu);
 +    }
 +
 +    @Override
 +    public boolean dispatchKeyEvent(KeyEvent event) {
 +        if (getSherlock().dispatchKeyEvent(event)) {
 +            return true;
 +        }
 +        return super.dispatchKeyEvent(event);
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Native menu handling
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    public MenuInflater getSupportMenuInflater() {
 +        return getSherlock().getMenuInflater();
 +    }
 +
 +    public void invalidateOptionsMenu() {
 +        getSherlock().dispatchInvalidateOptionsMenu();
 +    }
 +
 +    public void supportInvalidateOptionsMenu() {
 +        invalidateOptionsMenu();
 +    }
 +
 +    @Override
 +    public final boolean onCreateOptionsMenu(android.view.Menu menu) {
 +        return getSherlock().dispatchCreateOptionsMenu(menu);
 +    }
 +
 +    @Override
 +    public final boolean onPrepareOptionsMenu(android.view.Menu menu) {
 +        return getSherlock().dispatchPrepareOptionsMenu(menu);
 +    }
 +
 +    @Override
 +    public final boolean onOptionsItemSelected(android.view.MenuItem item) {
 +        return getSherlock().dispatchOptionsItemSelected(item);
 +    }
 +
 +    @Override
 +    public void openOptionsMenu() {
 +        if (!getSherlock().dispatchOpenOptionsMenu()) {
 +            super.openOptionsMenu();
 +        }
 +    }
 +
 +    @Override
 +    public void closeOptionsMenu() {
 +        if (!getSherlock().dispatchCloseOptionsMenu()) {
 +            super.closeOptionsMenu();
 +        }
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Sherlock menu handling
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    @Override
 +    public boolean onCreatePanelMenu(int featureId, Menu menu) {
 +        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
 +            return onCreateOptionsMenu(menu);
 +        }
 +        return false;
 +    }
 +
 +    public boolean onCreateOptionsMenu(Menu menu) {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean onPreparePanel(int featureId, View view, Menu menu) {
 +        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
 +            return onPrepareOptionsMenu(menu);
 +        }
 +        return false;
 +    }
 +
 +    public boolean onPrepareOptionsMenu(Menu menu) {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean onMenuItemSelected(int featureId, MenuItem item) {
 +        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
 +            return onOptionsItemSelected(item);
 +        }
 +        return false;
 +    }
 +
 +    public boolean onOptionsItemSelected(MenuItem item) {
 +        return false;
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Content
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    @Override
 +    public void addContentView(View view, LayoutParams params) {
 +        getSherlock().addContentView(view, params);
 +    }
 +
 +    @Override
 +    public void setContentView(int layoutResId) {
 +        getSherlock().setContentView(layoutResId);
 +    }
 +
 +    @Override
 +    public void setContentView(View view, LayoutParams params) {
 +        getSherlock().setContentView(view, params);
 +    }
 +
 +    @Override
 +    public void setContentView(View view) {
 +        getSherlock().setContentView(view);
 +    }
 +
 +    public void requestWindowFeature(long featureId) {
 +        getSherlock().requestFeature((int)featureId);
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Progress Indication
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    public void setSupportProgress(int progress) {
 +        getSherlock().setProgress(progress);
 +    }
 +
 +    public void setSupportProgressBarIndeterminate(boolean indeterminate) {
 +        getSherlock().setProgressBarIndeterminate(indeterminate);
 +    }
 +
 +    public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
 +        getSherlock().setProgressBarIndeterminateVisibility(visible);
 +    }
 +
 +    public void setSupportProgressBarVisibility(boolean visible) {
 +        getSherlock().setProgressBarVisibility(visible);
 +    }
 +
 +    public void setSupportSecondaryProgress(int secondaryProgress) {
 +        getSherlock().setSecondaryProgress(secondaryProgress);
 +    }
 +}
 diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragment.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragment.java new file mode 100644 index 000000000..0f24e9c85 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragment.java @@ -0,0 +1,68 @@ +package com.actionbarsherlock.app; + +import android.app.Activity; +import android.support.v4.app.Fragment; +import com.actionbarsherlock.internal.view.menu.MenuItemWrapper; +import com.actionbarsherlock.internal.view.menu.MenuWrapper; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuInflater; +import com.actionbarsherlock.view.MenuItem; + +import static com.actionbarsherlock.app.SherlockFragmentActivity.OnCreateOptionsMenuListener; +import static com.actionbarsherlock.app.SherlockFragmentActivity.OnOptionsItemSelectedListener; +import static com.actionbarsherlock.app.SherlockFragmentActivity.OnPrepareOptionsMenuListener; + +public class SherlockFragment extends Fragment implements OnCreateOptionsMenuListener, OnPrepareOptionsMenuListener, OnOptionsItemSelectedListener { +    private SherlockFragmentActivity mActivity; + +    public SherlockFragmentActivity getSherlockActivity() { +        return mActivity; +    } + +    @Override +    public void onAttach(Activity activity) { +        if (!(activity instanceof SherlockFragmentActivity)) { +            throw new IllegalStateException(getClass().getSimpleName() + " must be attached to a SherlockFragmentActivity."); +        } +        mActivity = (SherlockFragmentActivity)activity; + +        super.onAttach(activity); +    } + +    @Override +    public void onDetach() { +        mActivity = null; +        super.onDetach(); +    } + +    @Override +    public final void onCreateOptionsMenu(android.view.Menu menu, android.view.MenuInflater inflater) { +        onCreateOptionsMenu(new MenuWrapper(menu), mActivity.getSupportMenuInflater()); +    } + +    @Override +    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { +        //Nothing to see here. +    } + +    @Override +    public final void onPrepareOptionsMenu(android.view.Menu menu) { +        onPrepareOptionsMenu(new MenuWrapper(menu)); +    } + +    @Override +    public void onPrepareOptionsMenu(Menu menu) { +        //Nothing to see here. +    } + +    @Override +    public final boolean onOptionsItemSelected(android.view.MenuItem item) { +        return onOptionsItemSelected(new MenuItemWrapper(item)); +    } + +    @Override +    public boolean onOptionsItemSelected(MenuItem item) { +        //Nothing to see here. +        return false; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragmentActivity.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragmentActivity.java new file mode 100644 index 000000000..3d092f033 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragmentActivity.java @@ -0,0 +1,303 @@ +package com.actionbarsherlock.app; + +import android.content.res.Configuration; +import android.os.Bundle; +import android.support.v4.app.Watson; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.view.Window; +import com.actionbarsherlock.ActionBarSherlock; +import com.actionbarsherlock.view.ActionMode; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuInflater; +import com.actionbarsherlock.view.MenuItem; + +import static com.actionbarsherlock.ActionBarSherlock.OnActionModeFinishedListener; +import static com.actionbarsherlock.ActionBarSherlock.OnActionModeStartedListener; + +/** @see {@link android.support.v4.app.Watson} */ +public class SherlockFragmentActivity extends Watson implements OnActionModeStartedListener, OnActionModeFinishedListener { +    private static final boolean DEBUG = false; +    private static final String TAG = "SherlockFragmentActivity"; + +    private ActionBarSherlock mSherlock; +    private boolean mIgnoreNativeCreate = false; +    private boolean mIgnoreNativePrepare = false; +    private boolean mIgnoreNativeSelected = false; + +    protected final ActionBarSherlock getSherlock() { +        if (mSherlock == null) { +            mSherlock = ActionBarSherlock.wrap(this, ActionBarSherlock.FLAG_DELEGATE); +        } +        return mSherlock; +    } + + +    /////////////////////////////////////////////////////////////////////////// +    // Action bar and mode +    /////////////////////////////////////////////////////////////////////////// + +    public ActionBar getSupportActionBar() { +        return getSherlock().getActionBar(); +    } + +    public ActionMode startActionMode(ActionMode.Callback callback) { +        return getSherlock().startActionMode(callback); +    } + +    @Override +    public void onActionModeStarted(ActionMode mode) {} + +    @Override +    public void onActionModeFinished(ActionMode mode) {} + + +    /////////////////////////////////////////////////////////////////////////// +    // General lifecycle/callback dispatching +    /////////////////////////////////////////////////////////////////////////// + +    @Override +    public void onConfigurationChanged(Configuration newConfig) { +        super.onConfigurationChanged(newConfig); +        getSherlock().dispatchConfigurationChanged(newConfig); +    } + +    @Override +    protected void onPostResume() { +        super.onPostResume(); +        getSherlock().dispatchPostResume(); +    } + +    @Override +    protected void onPause() { +        getSherlock().dispatchPause(); +        super.onPause(); +    } + +    @Override +    protected void onStop() { +        getSherlock().dispatchStop(); +        super.onStop(); +    } + +    @Override +    protected void onDestroy() { +        getSherlock().dispatchDestroy(); +        super.onDestroy(); +    } + +    @Override +    protected void onPostCreate(Bundle savedInstanceState) { +        getSherlock().dispatchPostCreate(savedInstanceState); +        super.onPostCreate(savedInstanceState); +    } + +    @Override +    protected void onTitleChanged(CharSequence title, int color) { +        getSherlock().dispatchTitleChanged(title, color); +        super.onTitleChanged(title, color); +    } + +    @Override +    public final boolean onMenuOpened(int featureId, android.view.Menu menu) { +        if (getSherlock().dispatchMenuOpened(featureId, menu)) { +            return true; +        } +        return super.onMenuOpened(featureId, menu); +    } + +    @Override +    public void onPanelClosed(int featureId, android.view.Menu menu) { +        getSherlock().dispatchPanelClosed(featureId, menu); +        super.onPanelClosed(featureId, menu); +    } + +    @Override +    public boolean dispatchKeyEvent(KeyEvent event) { +        if (getSherlock().dispatchKeyEvent(event)) { +            return true; +        } +        return super.dispatchKeyEvent(event); +    } + +    @Override +    protected void onSaveInstanceState(Bundle outState) { +        super.onSaveInstanceState(outState); +        getSherlock().dispatchSaveInstanceState(outState); +    } + +    @Override +    protected void onRestoreInstanceState(Bundle savedInstanceState) { +        super.onRestoreInstanceState(savedInstanceState); +        getSherlock().dispatchRestoreInstanceState(savedInstanceState); +    } + +    /////////////////////////////////////////////////////////////////////////// +    // Native menu handling +    /////////////////////////////////////////////////////////////////////////// + +    public MenuInflater getSupportMenuInflater() { +        if (DEBUG) Log.d(TAG, "[getSupportMenuInflater]"); + +        return getSherlock().getMenuInflater(); +    } + +    public void invalidateOptionsMenu() { +        if (DEBUG) Log.d(TAG, "[invalidateOptionsMenu]"); + +        getSherlock().dispatchInvalidateOptionsMenu(); +    } + +    public void supportInvalidateOptionsMenu() { +        if (DEBUG) Log.d(TAG, "[supportInvalidateOptionsMenu]"); + +        invalidateOptionsMenu(); +    } + +    @Override +    public final boolean onCreatePanelMenu(int featureId, android.view.Menu menu) { +        if (DEBUG) Log.d(TAG, "[onCreatePanelMenu] featureId: " + featureId + ", menu: " + menu); + +        if (featureId == Window.FEATURE_OPTIONS_PANEL && !mIgnoreNativeCreate) { +            mIgnoreNativeCreate = true; +            boolean result = getSherlock().dispatchCreateOptionsMenu(menu); +            mIgnoreNativeCreate = false; + +            if (DEBUG) Log.d(TAG, "[onCreatePanelMenu] returning " + result); +            return result; +        } +        return super.onCreatePanelMenu(featureId, menu); +    } + +    @Override +    public final boolean onCreateOptionsMenu(android.view.Menu menu) { +        return true; +    } + +    @Override +    public final boolean onPreparePanel(int featureId, View view, android.view.Menu menu) { +        if (DEBUG) Log.d(TAG, "[onPreparePanel] featureId: " + featureId + ", view: " + view + ", menu: " + menu); + +        if (featureId == Window.FEATURE_OPTIONS_PANEL && !mIgnoreNativePrepare) { +            mIgnoreNativePrepare = true; +            boolean result = getSherlock().dispatchPrepareOptionsMenu(menu); +            mIgnoreNativePrepare = false; + +            if (DEBUG) Log.d(TAG, "[onPreparePanel] returning " + result); +            return result; +        } +        return super.onPreparePanel(featureId, view, menu); +    } + +    @Override +    public final boolean onPrepareOptionsMenu(android.view.Menu menu) { +        return true; +    } + +    @Override +    public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) { +        if (DEBUG) Log.d(TAG, "[onMenuItemSelected] featureId: " + featureId + ", item: " + item); + +        if (featureId == Window.FEATURE_OPTIONS_PANEL && !mIgnoreNativeSelected) { +            mIgnoreNativeSelected = true; +            boolean result = getSherlock().dispatchOptionsItemSelected(item); +            mIgnoreNativeSelected = false; + +            if (DEBUG) Log.d(TAG, "[onMenuItemSelected] returning " + result); +            return result; +        } +        return super.onMenuItemSelected(featureId, item); +    } + +    @Override +    public final boolean onOptionsItemSelected(android.view.MenuItem item) { +        return false; +    } + +    @Override +    public void openOptionsMenu() { +        if (!getSherlock().dispatchOpenOptionsMenu()) { +            super.openOptionsMenu(); +        } +    } + +    @Override +    public void closeOptionsMenu() { +        if (!getSherlock().dispatchCloseOptionsMenu()) { +            super.closeOptionsMenu(); +        } +    } + + +    /////////////////////////////////////////////////////////////////////////// +    // Sherlock menu handling +    /////////////////////////////////////////////////////////////////////////// + +    public boolean onCreateOptionsMenu(Menu menu) { +        return true; +    } + +    public boolean onPrepareOptionsMenu(Menu menu) { +        return true; +    } + +    public boolean onOptionsItemSelected(MenuItem item) { +        return false; +    } + + +    /////////////////////////////////////////////////////////////////////////// +    // Content +    /////////////////////////////////////////////////////////////////////////// + +    @Override +    public void addContentView(View view, LayoutParams params) { +        getSherlock().addContentView(view, params); +    } + +    @Override +    public void setContentView(int layoutResId) { +        getSherlock().setContentView(layoutResId); +    } + +    @Override +    public void setContentView(View view, LayoutParams params) { +        getSherlock().setContentView(view, params); +    } + +    @Override +    public void setContentView(View view) { +        getSherlock().setContentView(view); +    } + +    public void requestWindowFeature(long featureId) { +        getSherlock().requestFeature((int)featureId); +    } + + +    /////////////////////////////////////////////////////////////////////////// +    // Progress Indication +    /////////////////////////////////////////////////////////////////////////// + +    public void setSupportProgress(int progress) { +        getSherlock().setProgress(progress); +    } + +    public void setSupportProgressBarIndeterminate(boolean indeterminate) { +        getSherlock().setProgressBarIndeterminate(indeterminate); +    } + +    public void setSupportProgressBarIndeterminateVisibility(boolean visible) { +        getSherlock().setProgressBarIndeterminateVisibility(visible); +    } + +    public void setSupportProgressBarVisibility(boolean visible) { +        getSherlock().setProgressBarVisibility(visible); +    } + +    public void setSupportSecondaryProgress(int secondaryProgress) { +        getSherlock().setSecondaryProgress(secondaryProgress); +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListActivity.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListActivity.java new file mode 100644 index 000000000..aba6d85e8 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListActivity.java @@ -0,0 +1,270 @@ +package com.actionbarsherlock.app;
 +
 +import android.app.ListActivity;
 +import android.content.res.Configuration;
 +import android.os.Bundle;
 +import android.view.KeyEvent;
 +import android.view.View;
 +import android.view.Window;
 +import android.view.ViewGroup.LayoutParams;
 +import com.actionbarsherlock.ActionBarSherlock;
 +import com.actionbarsherlock.ActionBarSherlock.OnActionModeFinishedListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnActionModeStartedListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnCreatePanelMenuListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnMenuItemSelectedListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnPreparePanelListener;
 +import com.actionbarsherlock.view.ActionMode;
 +import com.actionbarsherlock.view.Menu;
 +import com.actionbarsherlock.view.MenuInflater;
 +import com.actionbarsherlock.view.MenuItem;
 +
 +public abstract class SherlockListActivity extends ListActivity implements OnCreatePanelMenuListener, OnPreparePanelListener, OnMenuItemSelectedListener, OnActionModeStartedListener, OnActionModeFinishedListener {
 +    private ActionBarSherlock mSherlock;
 +
 +    protected final ActionBarSherlock getSherlock() {
 +        if (mSherlock == null) {
 +            mSherlock = ActionBarSherlock.wrap(this, ActionBarSherlock.FLAG_DELEGATE);
 +        }
 +        return mSherlock;
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Action bar and mode
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    public ActionBar getSupportActionBar() {
 +        return getSherlock().getActionBar();
 +    }
 +
 +    public ActionMode startActionMode(ActionMode.Callback callback) {
 +        return getSherlock().startActionMode(callback);
 +    }
 +
 +    @Override
 +    public void onActionModeStarted(ActionMode mode) {}
 +
 +    @Override
 +    public void onActionModeFinished(ActionMode mode) {}
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // General lifecycle/callback dispatching
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    @Override
 +    public void onConfigurationChanged(Configuration newConfig) {
 +        super.onConfigurationChanged(newConfig);
 +        getSherlock().dispatchConfigurationChanged(newConfig);
 +    }
 +
 +    @Override
 +    protected void onPostResume() {
 +        super.onPostResume();
 +        getSherlock().dispatchPostResume();
 +    }
 +
 +    @Override
 +    protected void onPause() {
 +        getSherlock().dispatchPause();
 +        super.onPause();
 +    }
 +
 +    @Override
 +    protected void onStop() {
 +        getSherlock().dispatchStop();
 +        super.onStop();
 +    }
 +
 +    @Override
 +    protected void onDestroy() {
 +        getSherlock().dispatchDestroy();
 +        super.onDestroy();
 +    }
 +
 +    @Override
 +    protected void onPostCreate(Bundle savedInstanceState) {
 +        getSherlock().dispatchPostCreate(savedInstanceState);
 +        super.onPostCreate(savedInstanceState);
 +    }
 +
 +    @Override
 +    protected void onTitleChanged(CharSequence title, int color) {
 +        getSherlock().dispatchTitleChanged(title, color);
 +        super.onTitleChanged(title, color);
 +    }
 +
 +    @Override
 +    public final boolean onMenuOpened(int featureId, android.view.Menu menu) {
 +        if (getSherlock().dispatchMenuOpened(featureId, menu)) {
 +            return true;
 +        }
 +        return super.onMenuOpened(featureId, menu);
 +    }
 +
 +    @Override
 +    public void onPanelClosed(int featureId, android.view.Menu menu) {
 +        getSherlock().dispatchPanelClosed(featureId, menu);
 +        super.onPanelClosed(featureId, menu);
 +    }
 +
 +    @Override
 +    public boolean dispatchKeyEvent(KeyEvent event) {
 +        if (getSherlock().dispatchKeyEvent(event)) {
 +            return true;
 +        }
 +        return super.dispatchKeyEvent(event);
 +    }
 +
 +    @Override
 +    protected void onSaveInstanceState(Bundle outState) {
 +        super.onSaveInstanceState(outState);
 +        getSherlock().dispatchSaveInstanceState(outState);
 +    }
 +
 +    @Override
 +    protected void onRestoreInstanceState(Bundle savedInstanceState) {
 +        super.onRestoreInstanceState(savedInstanceState);
 +        getSherlock().dispatchRestoreInstanceState(savedInstanceState);
 +    }
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Native menu handling
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    public MenuInflater getSupportMenuInflater() {
 +        return getSherlock().getMenuInflater();
 +    }
 +
 +    public void invalidateOptionsMenu() {
 +        getSherlock().dispatchInvalidateOptionsMenu();
 +    }
 +
 +    public void supportInvalidateOptionsMenu() {
 +        invalidateOptionsMenu();
 +    }
 +
 +    @Override
 +    public final boolean onCreateOptionsMenu(android.view.Menu menu) {
 +        return getSherlock().dispatchCreateOptionsMenu(menu);
 +    }
 +
 +    @Override
 +    public final boolean onPrepareOptionsMenu(android.view.Menu menu) {
 +        return getSherlock().dispatchPrepareOptionsMenu(menu);
 +    }
 +
 +    @Override
 +    public final boolean onOptionsItemSelected(android.view.MenuItem item) {
 +        return getSherlock().dispatchOptionsItemSelected(item);
 +    }
 +
 +    @Override
 +    public void openOptionsMenu() {
 +        if (!getSherlock().dispatchOpenOptionsMenu()) {
 +            super.openOptionsMenu();
 +        }
 +    }
 +
 +    @Override
 +    public void closeOptionsMenu() {
 +        if (!getSherlock().dispatchCloseOptionsMenu()) {
 +            super.closeOptionsMenu();
 +        }
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Sherlock menu handling
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    @Override
 +    public boolean onCreatePanelMenu(int featureId, Menu menu) {
 +        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
 +            return onCreateOptionsMenu(menu);
 +        }
 +        return false;
 +    }
 +
 +    public boolean onCreateOptionsMenu(Menu menu) {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean onPreparePanel(int featureId, View view, Menu menu) {
 +        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
 +            return onPrepareOptionsMenu(menu);
 +        }
 +        return false;
 +    }
 +
 +    public boolean onPrepareOptionsMenu(Menu menu) {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean onMenuItemSelected(int featureId, MenuItem item) {
 +        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
 +            return onOptionsItemSelected(item);
 +        }
 +        return false;
 +    }
 +
 +    public boolean onOptionsItemSelected(MenuItem item) {
 +        return false;
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Content
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    @Override
 +    public void addContentView(View view, LayoutParams params) {
 +        getSherlock().addContentView(view, params);
 +    }
 +
 +    @Override
 +    public void setContentView(int layoutResId) {
 +        getSherlock().setContentView(layoutResId);
 +    }
 +
 +    @Override
 +    public void setContentView(View view, LayoutParams params) {
 +        getSherlock().setContentView(view, params);
 +    }
 +
 +    @Override
 +    public void setContentView(View view) {
 +        getSherlock().setContentView(view);
 +    }
 +
 +    public void requestWindowFeature(long featureId) {
 +        getSherlock().requestFeature((int)featureId);
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Progress Indication
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    public void setSupportProgress(int progress) {
 +        getSherlock().setProgress(progress);
 +    }
 +
 +    public void setSupportProgressBarIndeterminate(boolean indeterminate) {
 +        getSherlock().setProgressBarIndeterminate(indeterminate);
 +    }
 +
 +    public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
 +        getSherlock().setProgressBarIndeterminateVisibility(visible);
 +    }
 +
 +    public void setSupportProgressBarVisibility(boolean visible) {
 +        getSherlock().setProgressBarVisibility(visible);
 +    }
 +
 +    public void setSupportSecondaryProgress(int secondaryProgress) {
 +        getSherlock().setSecondaryProgress(secondaryProgress);
 +    }
 +}
 diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListFragment.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListFragment.java new file mode 100644 index 000000000..13ca3c49f --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListFragment.java @@ -0,0 +1,68 @@ +package com.actionbarsherlock.app; + +import android.app.Activity; +import android.support.v4.app.ListFragment; +import com.actionbarsherlock.internal.view.menu.MenuItemWrapper; +import com.actionbarsherlock.internal.view.menu.MenuWrapper; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuInflater; +import com.actionbarsherlock.view.MenuItem; + +import static com.actionbarsherlock.app.SherlockFragmentActivity.OnCreateOptionsMenuListener; +import static com.actionbarsherlock.app.SherlockFragmentActivity.OnOptionsItemSelectedListener; +import static com.actionbarsherlock.app.SherlockFragmentActivity.OnPrepareOptionsMenuListener; + +public class SherlockListFragment extends ListFragment implements OnCreateOptionsMenuListener, OnPrepareOptionsMenuListener, OnOptionsItemSelectedListener { +    private SherlockFragmentActivity mActivity; + +    public SherlockFragmentActivity getSherlockActivity() { +        return mActivity; +    } + +    @Override +    public void onAttach(Activity activity) { +        if (!(activity instanceof SherlockFragmentActivity)) { +            throw new IllegalStateException(getClass().getSimpleName() + " must be attached to a SherlockFragmentActivity."); +        } +        mActivity = (SherlockFragmentActivity)activity; + +        super.onAttach(activity); +    } + +    @Override +    public void onDetach() { +        mActivity = null; +        super.onDetach(); +    } + +    @Override +    public final void onCreateOptionsMenu(android.view.Menu menu, android.view.MenuInflater inflater) { +        onCreateOptionsMenu(new MenuWrapper(menu), mActivity.getSupportMenuInflater()); +    } + +    @Override +    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { +        //Nothing to see here. +    } + +    @Override +    public final void onPrepareOptionsMenu(android.view.Menu menu) { +        onPrepareOptionsMenu(new MenuWrapper(menu)); +    } + +    @Override +    public void onPrepareOptionsMenu(Menu menu) { +        //Nothing to see here. +    } + +    @Override +    public final boolean onOptionsItemSelected(android.view.MenuItem item) { +        return onOptionsItemSelected(new MenuItemWrapper(item)); +    } + +    @Override +    public boolean onOptionsItemSelected(MenuItem item) { +        //Nothing to see here. +        return false; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockPreferenceActivity.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockPreferenceActivity.java new file mode 100644 index 000000000..bee72cb25 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockPreferenceActivity.java @@ -0,0 +1,270 @@ +package com.actionbarsherlock.app;
 +
 +import android.content.res.Configuration;
 +import android.os.Bundle;
 +import android.preference.PreferenceActivity;
 +import android.view.KeyEvent;
 +import android.view.View;
 +import android.view.ViewGroup.LayoutParams;
 +import android.view.Window;
 +import com.actionbarsherlock.ActionBarSherlock;
 +import com.actionbarsherlock.ActionBarSherlock.OnActionModeFinishedListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnActionModeStartedListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnCreatePanelMenuListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnMenuItemSelectedListener;
 +import com.actionbarsherlock.ActionBarSherlock.OnPreparePanelListener;
 +import com.actionbarsherlock.view.ActionMode;
 +import com.actionbarsherlock.view.Menu;
 +import com.actionbarsherlock.view.MenuInflater;
 +import com.actionbarsherlock.view.MenuItem;
 +
 +public abstract class SherlockPreferenceActivity extends PreferenceActivity implements OnCreatePanelMenuListener, OnPreparePanelListener, OnMenuItemSelectedListener, OnActionModeStartedListener, OnActionModeFinishedListener {
 +    private ActionBarSherlock mSherlock;
 +
 +    protected final ActionBarSherlock getSherlock() {
 +        if (mSherlock == null) {
 +            mSherlock = ActionBarSherlock.wrap(this, ActionBarSherlock.FLAG_DELEGATE);
 +        }
 +        return mSherlock;
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Action bar and mode
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    public ActionBar getSupportActionBar() {
 +        return getSherlock().getActionBar();
 +    }
 +
 +    public ActionMode startActionMode(ActionMode.Callback callback) {
 +        return getSherlock().startActionMode(callback);
 +    }
 +
 +    @Override
 +    public void onActionModeStarted(ActionMode mode) {}
 +
 +    @Override
 +    public void onActionModeFinished(ActionMode mode) {}
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // General lifecycle/callback dispatching
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    @Override
 +    public void onConfigurationChanged(Configuration newConfig) {
 +        super.onConfigurationChanged(newConfig);
 +        getSherlock().dispatchConfigurationChanged(newConfig);
 +    }
 +
 +    @Override
 +    protected void onPostResume() {
 +        super.onPostResume();
 +        getSherlock().dispatchPostResume();
 +    }
 +
 +    @Override
 +    protected void onPause() {
 +        getSherlock().dispatchPause();
 +        super.onPause();
 +    }
 +
 +    @Override
 +    protected void onStop() {
 +        getSherlock().dispatchStop();
 +        super.onStop();
 +    }
 +
 +    @Override
 +    protected void onDestroy() {
 +        getSherlock().dispatchDestroy();
 +        super.onDestroy();
 +    }
 +
 +    @Override
 +    protected void onPostCreate(Bundle savedInstanceState) {
 +        getSherlock().dispatchPostCreate(savedInstanceState);
 +        super.onPostCreate(savedInstanceState);
 +    }
 +
 +    @Override
 +    protected void onTitleChanged(CharSequence title, int color) {
 +        getSherlock().dispatchTitleChanged(title, color);
 +        super.onTitleChanged(title, color);
 +    }
 +
 +    @Override
 +    public final boolean onMenuOpened(int featureId, android.view.Menu menu) {
 +        if (getSherlock().dispatchMenuOpened(featureId, menu)) {
 +            return true;
 +        }
 +        return super.onMenuOpened(featureId, menu);
 +    }
 +
 +    @Override
 +    public void onPanelClosed(int featureId, android.view.Menu menu) {
 +        getSherlock().dispatchPanelClosed(featureId, menu);
 +        super.onPanelClosed(featureId, menu);
 +    }
 +
 +    @Override
 +    public boolean dispatchKeyEvent(KeyEvent event) {
 +        if (getSherlock().dispatchKeyEvent(event)) {
 +            return true;
 +        }
 +        return super.dispatchKeyEvent(event);
 +    }
 +
 +    @Override
 +    protected void onSaveInstanceState(Bundle outState) {
 +        super.onSaveInstanceState(outState);
 +        getSherlock().dispatchSaveInstanceState(outState);
 +    }
 +
 +    @Override
 +    protected void onRestoreInstanceState(Bundle savedInstanceState) {
 +        super.onRestoreInstanceState(savedInstanceState);
 +        getSherlock().dispatchRestoreInstanceState(savedInstanceState);
 +    }
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Native menu handling
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    public MenuInflater getSupportMenuInflater() {
 +        return getSherlock().getMenuInflater();
 +    }
 +
 +    public void invalidateOptionsMenu() {
 +        getSherlock().dispatchInvalidateOptionsMenu();
 +    }
 +
 +    public void supportInvalidateOptionsMenu() {
 +        invalidateOptionsMenu();
 +    }
 +
 +    @Override
 +    public final boolean onCreateOptionsMenu(android.view.Menu menu) {
 +        return getSherlock().dispatchCreateOptionsMenu(menu);
 +    }
 +
 +    @Override
 +    public final boolean onPrepareOptionsMenu(android.view.Menu menu) {
 +        return getSherlock().dispatchPrepareOptionsMenu(menu);
 +    }
 +
 +    @Override
 +    public final boolean onOptionsItemSelected(android.view.MenuItem item) {
 +        return getSherlock().dispatchOptionsItemSelected(item);
 +    }
 +
 +    @Override
 +    public void openOptionsMenu() {
 +        if (!getSherlock().dispatchOpenOptionsMenu()) {
 +            super.openOptionsMenu();
 +        }
 +    }
 +
 +    @Override
 +    public void closeOptionsMenu() {
 +        if (!getSherlock().dispatchCloseOptionsMenu()) {
 +            super.closeOptionsMenu();
 +        }
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Sherlock menu handling
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    @Override
 +    public boolean onCreatePanelMenu(int featureId, Menu menu) {
 +        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
 +            return onCreateOptionsMenu(menu);
 +        }
 +        return false;
 +    }
 +
 +    public boolean onCreateOptionsMenu(Menu menu) {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean onPreparePanel(int featureId, View view, Menu menu) {
 +        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
 +            return onPrepareOptionsMenu(menu);
 +        }
 +        return false;
 +    }
 +
 +    public boolean onPrepareOptionsMenu(Menu menu) {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean onMenuItemSelected(int featureId, MenuItem item) {
 +        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
 +            return onOptionsItemSelected(item);
 +        }
 +        return false;
 +    }
 +
 +    public boolean onOptionsItemSelected(MenuItem item) {
 +        return false;
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Content
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    @Override
 +    public void addContentView(View view, LayoutParams params) {
 +        getSherlock().addContentView(view, params);
 +    }
 +
 +    @Override
 +    public void setContentView(int layoutResId) {
 +        getSherlock().setContentView(layoutResId);
 +    }
 +
 +    @Override
 +    public void setContentView(View view, LayoutParams params) {
 +        getSherlock().setContentView(view, params);
 +    }
 +
 +    @Override
 +    public void setContentView(View view) {
 +        getSherlock().setContentView(view);
 +    }
 +
 +    public void requestWindowFeature(long featureId) {
 +        getSherlock().requestFeature((int)featureId);
 +    }
 +
 +
 +    ///////////////////////////////////////////////////////////////////////////
 +    // Progress Indication
 +    ///////////////////////////////////////////////////////////////////////////
 +
 +    public void setSupportProgress(int progress) {
 +        getSherlock().setProgress(progress);
 +    }
 +
 +    public void setSupportProgressBarIndeterminate(boolean indeterminate) {
 +        getSherlock().setProgressBarIndeterminate(indeterminate);
 +    }
 +
 +    public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
 +        getSherlock().setProgressBarIndeterminateVisibility(visible);
 +    }
 +
 +    public void setSupportProgressBarVisibility(boolean visible) {
 +        getSherlock().setProgressBarVisibility(visible);
 +    }
 +
 +    public void setSupportSecondaryProgress(int secondaryProgress) {
 +        getSherlock().setSecondaryProgress(secondaryProgress);
 +    }
 +}
 diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockCompat.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockCompat.java new file mode 100644 index 000000000..5e69275c7 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockCompat.java @@ -0,0 +1,1203 @@ +package com.actionbarsherlock.internal; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import org.xmlpull.v1.XmlPullParser; +import android.app.Activity; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.os.Bundle; +import android.util.AndroidRuntimeException; +import android.util.Log; +import android.util.TypedValue; +import android.view.ContextThemeWrapper; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewStub; +import android.view.Window; +import android.view.accessibility.AccessibilityEvent; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.FrameLayout; +import android.widget.TextView; +import com.actionbarsherlock.ActionBarSherlock; +import com.actionbarsherlock.R; +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.internal.app.ActionBarImpl; +import com.actionbarsherlock.internal.view.StandaloneActionMode; +import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter; +import com.actionbarsherlock.internal.view.menu.MenuBuilder; +import com.actionbarsherlock.internal.view.menu.MenuItemImpl; +import com.actionbarsherlock.internal.view.menu.MenuPresenter; +import com.actionbarsherlock.internal.widget.ActionBarContainer; +import com.actionbarsherlock.internal.widget.ActionBarContextView; +import com.actionbarsherlock.internal.widget.ActionBarView; +import com.actionbarsherlock.internal.widget.IcsProgressBar; +import com.actionbarsherlock.view.ActionMode; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +@ActionBarSherlock.Implementation(api = 7) +public class ActionBarSherlockCompat extends ActionBarSherlock implements MenuBuilder.Callback, com.actionbarsherlock.view.Window.Callback, MenuPresenter.Callback, android.view.MenuItem.OnMenuItemClickListener { +    /** Window features which are enabled by default. */ +    protected static final int DEFAULT_FEATURES = 0; + +    static private final String PANELS_TAG = "sherlock:Panels"; + +    public ActionBarSherlockCompat(Activity activity, int flags) { +        super(activity, flags); +    } + + +    /////////////////////////////////////////////////////////////////////////// +    // Properties +    /////////////////////////////////////////////////////////////////////////// + +    /** Whether or not the device has a dedicated menu key button. */ +    private boolean mReserveOverflow; +    /** Lazy-load indicator for {@link #mReserveOverflow}. */ +    private boolean mReserveOverflowSet = false; + +    /** Current menu instance for managing action items. */ +    private MenuBuilder mMenu; +    /** Map between native options items and sherlock items. */ +    protected HashMap<android.view.MenuItem, MenuItemImpl> mNativeItemMap; + +    /** Parent view of the window decoration (action bar, mode, etc.). */ +    private ViewGroup mDecor; +    /** Parent view of the activity content. */ +    private ViewGroup mContentParent; + +    /** Whether or not the title is stable and can be displayed. */ +    private boolean mIsTitleReady = false; +    /** Whether or not the parent activity has been destroyed. */ +    private boolean mIsDestroyed = false; + +    /* Emulate PanelFeatureState */ +    private boolean mClosingActionMenu; +    private boolean mMenuIsPrepared; +    private boolean mMenuRefreshContent; +    private Bundle mMenuFrozenActionViewState; + +    /** Implementation which backs the action bar interface API. */ +    private ActionBarImpl aActionBar; +    /** Main action bar view which displays the core content. */ +    private ActionBarView wActionBar; +    /** Relevant window and action bar features flags. */ +    private int mFeatures = DEFAULT_FEATURES; +    /** Relevant user interface option flags. */ +    private int mUiOptions = 0; + +    /** Decor indeterminate progress indicator. */ +    private IcsProgressBar mCircularProgressBar; +    /** Decor progress indicator. */ +    private IcsProgressBar mHorizontalProgressBar; + +    /** Current displayed context action bar, if any. */ +    private ActionMode mActionMode; +    /** Parent view in which the context action bar is displayed. */ +    private ActionBarContextView mActionModeView; + +    /** Title view used with dialogs. */ +    private TextView mTitleView; +    /** Current activity title. */ +    private CharSequence mTitle = null; +    /** Whether or not this "activity" is floating (i.e., a dialog) */ +    private boolean mIsFloating; + + + +    /////////////////////////////////////////////////////////////////////////// +    // Instance methods +    /////////////////////////////////////////////////////////////////////////// + +    @Override +    public ActionBar getActionBar() { +        if (DEBUG) Log.d(TAG, "[getActionBar]"); + +        initActionBar(); +        return aActionBar; +    } + +    private void initActionBar() { +        if (DEBUG) Log.d(TAG, "[initActionBar]"); + +        // Initializing the window decor can change window feature flags. +        // Make sure that we have the correct set before performing the test below. +        if (mDecor == null) { +            installDecor(); +        } + +        if ((aActionBar != null) || !hasFeature(Window.FEATURE_ACTION_BAR) || hasFeature(Window.FEATURE_NO_TITLE) || mActivity.isChild()) { +            return; +        } + +        aActionBar = new ActionBarImpl(mActivity, mFeatures); + +        if (!mIsDelegate) { +            //We may never get another chance to set the title +            wActionBar.setWindowTitle(mActivity.getTitle()); +        } +    } + +    @Override +    protected Context getThemedContext() { +        return aActionBar.getThemedContext(); +    } + +    @Override +    public void setTitle(CharSequence title) { +        if (DEBUG) Log.d(TAG, "[setTitle] title: " + title); + +        dispatchTitleChanged(title, 0); +    } + +    @Override +    public ActionMode startActionMode(ActionMode.Callback callback) { +        if (DEBUG) Log.d(TAG, "[startActionMode] callback: " + callback); + +        if (mActionMode != null) { +            mActionMode.finish(); +        } + +        final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback); +        ActionMode mode = null; + +        //Emulate Activity's onWindowStartingActionMode: +        initActionBar(); +        if (aActionBar != null) { +            mode = aActionBar.startActionMode(wrappedCallback); +        } + +        if (mode != null) { +            mActionMode = mode; +        } else { +            if (mActionModeView == null) { +                ViewStub stub = (ViewStub)mDecor.findViewById(R.id.abs__action_mode_bar_stub); +                if (stub != null) { +                    mActionModeView = (ActionBarContextView)stub.inflate(); +                } +            } +            if (mActionModeView != null) { +                mActionModeView.killMode(); +                mode = new StandaloneActionMode(mActivity, mActionModeView, wrappedCallback, true); +                if (callback.onCreateActionMode(mode, mode.getMenu())) { +                    mode.invalidate(); +                    mActionModeView.initForMode(mode); +                    mActionModeView.setVisibility(View.VISIBLE); +                    mActionMode = mode; +                    mActionModeView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); +                } else { +                    mActionMode = null; +                } +            } +        } +        if (mActionMode != null && mActivity instanceof OnActionModeStartedListener) { +            ((OnActionModeStartedListener)mActivity).onActionModeStarted(mActionMode); +        } +        return mActionMode; +    } + + +    /////////////////////////////////////////////////////////////////////////// +    // Lifecycle and interaction callbacks for delegation +    /////////////////////////////////////////////////////////////////////////// + +    @Override +    public void dispatchConfigurationChanged(Configuration newConfig) { +        if (DEBUG) Log.d(TAG, "[dispatchConfigurationChanged] newConfig: " + newConfig); + +        if (aActionBar != null) { +            aActionBar.onConfigurationChanged(newConfig); +        } +    } + +    @Override +    public void dispatchPostResume() { +        if (DEBUG) Log.d(TAG, "[dispatchPostResume]"); + +        if (aActionBar != null) { +            aActionBar.setShowHideAnimationEnabled(true); +        } +    } + +    @Override +    public void dispatchPause() { +        if (DEBUG) Log.d(TAG, "[dispatchPause]"); + +        if (wActionBar != null && wActionBar.isOverflowMenuShowing()) { +            wActionBar.hideOverflowMenu(); +        } +    } + +    @Override +    public void dispatchStop() { +        if (DEBUG) Log.d(TAG, "[dispatchStop]"); + +        if (aActionBar != null) { +            aActionBar.setShowHideAnimationEnabled(false); +        } +    } + +    @Override +    public void dispatchInvalidateOptionsMenu() { +        if (DEBUG) Log.d(TAG, "[dispatchInvalidateOptionsMenu]"); + +        Bundle savedActionViewStates = null; +        if (mMenu != null) { +            savedActionViewStates = new Bundle(); +            mMenu.saveActionViewStates(savedActionViewStates); +            if (savedActionViewStates.size() > 0) { +                mMenuFrozenActionViewState = savedActionViewStates; +            } +            // This will be started again when the panel is prepared. +            mMenu.stopDispatchingItemsChanged(); +            mMenu.clear(); +        } +        mMenuRefreshContent = true; + +        // Prepare the options panel if we have an action bar +        if (wActionBar != null) { +            mMenuIsPrepared = false; +            preparePanel(); +        } +    } + +    @Override +    public boolean dispatchOpenOptionsMenu() { +        if (DEBUG) Log.d(TAG, "[dispatchOpenOptionsMenu]"); + +        if (!isReservingOverflow()) { +            return false; +        } + +        return wActionBar.showOverflowMenu(); +    } + +    @Override +    public boolean dispatchCloseOptionsMenu() { +        if (DEBUG) Log.d(TAG, "[dispatchCloseOptionsMenu]"); + +        if (!isReservingOverflow()) { +            return false; +        } + +        if (wActionBar != null) { +            return wActionBar.hideOverflowMenu(); +        } +        return false; +    } + +    @Override +    public void dispatchPostCreate(Bundle savedInstanceState) { +        if (DEBUG) Log.d(TAG, "[dispatchOnPostCreate]"); + +        if (mIsDelegate) { +            mIsTitleReady = true; +        } + +        if (mDecor == null) { +            initActionBar(); +        } +    } + +    @Override +    public boolean dispatchCreateOptionsMenu(android.view.Menu menu) { +        if (DEBUG) { +            Log.d(TAG, "[dispatchCreateOptionsMenu] android.view.Menu: " + menu); +            Log.d(TAG, "[dispatchCreateOptionsMenu] returning true"); +        } +        return true; +    } + +    @Override +    public boolean dispatchPrepareOptionsMenu(android.view.Menu menu) { +        if (DEBUG) Log.d(TAG, "[dispatchPrepareOptionsMenu] android.view.Menu: " + menu); + +        if (mActionMode != null) { +            return false; +        } + +        mMenuIsPrepared = false; +        if (!preparePanel()) { +            return false; +        } + +        if (isReservingOverflow()) { +            return false; +        } + +        if (mNativeItemMap == null) { +            mNativeItemMap = new HashMap<android.view.MenuItem, MenuItemImpl>(); +        } else { +            mNativeItemMap.clear(); +        } + +        if (mMenu == null) { +            return false; +        } + +        boolean result = mMenu.bindNativeOverflow(menu, this, mNativeItemMap); +        if (DEBUG) Log.d(TAG, "[dispatchPrepareOptionsMenu] returning " + result); +        return result; +    } + +    @Override +    public boolean dispatchOptionsItemSelected(android.view.MenuItem item) { +        throw new IllegalStateException("Native callback invoked. Create a test case and report!"); +    } + +    @Override +    public boolean dispatchMenuOpened(int featureId, android.view.Menu menu) { +        if (DEBUG) Log.d(TAG, "[dispatchMenuOpened] featureId: " + featureId + ", menu: " + menu); + +        if (featureId == Window.FEATURE_ACTION_BAR || featureId == Window.FEATURE_OPTIONS_PANEL) { +            if (aActionBar != null) { +                aActionBar.dispatchMenuVisibilityChanged(true); +            } +            return true; +        } + +        return false; +    } + +    @Override +    public void dispatchPanelClosed(int featureId, android.view.Menu menu){ +        if (DEBUG) Log.d(TAG, "[dispatchPanelClosed] featureId: " + featureId + ", menu: " + menu); + +        if (featureId == Window.FEATURE_ACTION_BAR || featureId == Window.FEATURE_OPTIONS_PANEL) { +            if (aActionBar != null) { +                aActionBar.dispatchMenuVisibilityChanged(false); +            } +        } +    } + +    @Override +    public void dispatchTitleChanged(CharSequence title, int color) { +        if (DEBUG) Log.d(TAG, "[dispatchTitleChanged] title: " + title + ", color: " + color); + +        if (!mIsDelegate || mIsTitleReady) { +            if (mTitleView != null) { +                mTitleView.setText(title); +            } else if (wActionBar != null) { +                wActionBar.setWindowTitle(title); +            } +        } + +        mTitle = title; +    } + +    @Override +    public boolean dispatchKeyEvent(KeyEvent event) { +        if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] event: " + event); + +        final int keyCode = event.getKeyCode(); + +        // Not handled by the view hierarchy, does the action bar want it +        // to cancel out of something special? +        if (keyCode == KeyEvent.KEYCODE_BACK) { +            final int action = event.getAction(); +            // Back cancels action modes first. +            if (mActionMode != null) { +                if (action == KeyEvent.ACTION_UP) { +                    mActionMode.finish(); +                } +                if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning true"); +                return true; +            } + +            // Next collapse any expanded action views. +            if (wActionBar != null && wActionBar.hasExpandedActionView()) { +                if (action == KeyEvent.ACTION_UP) { +                    wActionBar.collapseActionView(); +                } +                if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning true"); +                return true; +            } +        } + +        if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning false"); +        return false; +    } + +    @Override +    public void dispatchDestroy() { +        mIsDestroyed = true; +    } + +    @Override +    public void dispatchSaveInstanceState(Bundle outState) { +        if (mMenu != null) { +            mMenuFrozenActionViewState = new Bundle(); +            mMenu.saveActionViewStates(mMenuFrozenActionViewState); +        } +        outState.putParcelable(PANELS_TAG, mMenuFrozenActionViewState); +    } + +    @Override +    public void dispatchRestoreInstanceState(Bundle savedInstanceState) { +        mMenuFrozenActionViewState = savedInstanceState.getParcelable(PANELS_TAG); +    } + +    /////////////////////////////////////////////////////////////////////////// +    // Menu callback lifecycle and creation +    /////////////////////////////////////////////////////////////////////////// + +    private boolean preparePanel() { +        // Already prepared (isPrepared will be reset to false later) +        if (mMenuIsPrepared) { +            return true; +        } + +        // Init the panel state's menu--return false if init failed +        if (mMenu == null || mMenuRefreshContent) { +            if (mMenu == null) { +                if (!initializePanelMenu() || (mMenu == null)) { +                    return false; +                } +            } + +            if (wActionBar != null) { +                wActionBar.setMenu(mMenu, this); +            } + +            // Call callback, and return if it doesn't want to display menu. + +            // Creating the panel menu will involve a lot of manipulation; +            // don't dispatch change events to presenters until we're done. +            mMenu.stopDispatchingItemsChanged(); +            if (!callbackCreateOptionsMenu(mMenu)) { +                // Ditch the menu created above +                mMenu = null; + +                if (wActionBar != null) { +                    // Don't show it in the action bar either +                    wActionBar.setMenu(null, this); +                } + +                return false; +            } + +            mMenuRefreshContent = false; +        } + +        // Callback and return if the callback does not want to show the menu + +        // Preparing the panel menu can involve a lot of manipulation; +        // don't dispatch change events to presenters until we're done. +        mMenu.stopDispatchingItemsChanged(); + +        // Restore action view state before we prepare. This gives apps +        // an opportunity to override frozen/restored state in onPrepare. +        if (mMenuFrozenActionViewState != null) { +            mMenu.restoreActionViewStates(mMenuFrozenActionViewState); +            mMenuFrozenActionViewState = null; +        } + +        if (!callbackPrepareOptionsMenu(mMenu)) { +            if (wActionBar != null) { +                // The app didn't want to show the menu for now but it still exists. +                // Clear it out of the action bar. +                wActionBar.setMenu(null, this); +            } +            mMenu.startDispatchingItemsChanged(); +            return false; +        } + +        // Set the proper keymap +        KeyCharacterMap kmap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); +        mMenu.setQwertyMode(kmap.getKeyboardType() != KeyCharacterMap.NUMERIC); +        mMenu.startDispatchingItemsChanged(); + +        // Set other state +        mMenuIsPrepared = true; + +        return true; +    } + +    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { +        return callbackOptionsItemSelected(item); +    } + +    public void onMenuModeChange(MenuBuilder menu) { +        reopenMenu(true); +    } + +    private void reopenMenu(boolean toggleMenuMode) { +        if (wActionBar != null && wActionBar.isOverflowReserved()) { +            if (!wActionBar.isOverflowMenuShowing() || !toggleMenuMode) { +                if (wActionBar.getVisibility() == View.VISIBLE) { +                    if (callbackPrepareOptionsMenu(mMenu)) { +                        wActionBar.showOverflowMenu(); +                    } +                } +            } else { +                wActionBar.hideOverflowMenu(); +            } +            return; +        } +    } + +    private boolean initializePanelMenu() { +        Context context = mActivity;//getContext(); + +        // If we have an action bar, initialize the menu with a context themed for it. +        if (wActionBar != null) { +            TypedValue outValue = new TypedValue(); +            Resources.Theme currentTheme = context.getTheme(); +            currentTheme.resolveAttribute(R.attr.actionBarWidgetTheme, +                    outValue, true); +            final int targetThemeRes = outValue.resourceId; + +            if (targetThemeRes != 0 /*&& context.getThemeResId() != targetThemeRes*/) { +                context = new ContextThemeWrapper(context, targetThemeRes); +            } +        } + +        mMenu = new MenuBuilder(context); +        mMenu.setCallback(this); + +        return true; +    } + +    void checkCloseActionMenu(Menu menu) { +        if (mClosingActionMenu) { +            return; +        } + +        mClosingActionMenu = true; +        wActionBar.dismissPopupMenus(); +        //Callback cb = getCallback(); +        //if (cb != null && !isDestroyed()) { +        //    cb.onPanelClosed(FEATURE_ACTION_BAR, menu); +        //} +        mClosingActionMenu = false; +    } + +    @Override +    public boolean onOpenSubMenu(MenuBuilder subMenu) { +        return true; +    } + +    @Override +    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { +        checkCloseActionMenu(menu); +    } + +    @Override +    public boolean onMenuItemClick(android.view.MenuItem item) { +        if (DEBUG) Log.d(TAG, "[mNativeItemListener.onMenuItemClick] item: " + item); + +        final MenuItemImpl sherlockItem = mNativeItemMap.get(item); +        if (sherlockItem != null) { +            sherlockItem.invoke(); +        } else { +            Log.e(TAG, "Options item \"" + item + "\" not found in mapping"); +        } + +        return true; //Do not allow continuation of native handling +    } + +    @Override +    public boolean onMenuItemSelected(int featureId, MenuItem item) { +        return callbackOptionsItemSelected(item); +    } + + +    /////////////////////////////////////////////////////////////////////////// +    // Progress bar interaction and internal handling +    /////////////////////////////////////////////////////////////////////////// + +    @Override +    public void setProgressBarVisibility(boolean visible) { +        if (DEBUG) Log.d(TAG, "[setProgressBarVisibility] visible: " + visible); + +        setFeatureInt(Window.FEATURE_PROGRESS, visible ? Window.PROGRESS_VISIBILITY_ON : +            Window.PROGRESS_VISIBILITY_OFF); +    } + +    @Override +    public void setProgressBarIndeterminateVisibility(boolean visible) { +        if (DEBUG) Log.d(TAG, "[setProgressBarIndeterminateVisibility] visible: " + visible); + +        setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS, +                visible ? Window.PROGRESS_VISIBILITY_ON : Window.PROGRESS_VISIBILITY_OFF); +    } + +    @Override +    public void setProgressBarIndeterminate(boolean indeterminate) { +        if (DEBUG) Log.d(TAG, "[setProgressBarIndeterminate] indeterminate: " + indeterminate); + +        setFeatureInt(Window.FEATURE_PROGRESS, +                indeterminate ? Window.PROGRESS_INDETERMINATE_ON : Window.PROGRESS_INDETERMINATE_OFF); +    } + +    @Override +    public void setProgress(int progress) { +        if (DEBUG) Log.d(TAG, "[setProgress] progress: " + progress); + +        setFeatureInt(Window.FEATURE_PROGRESS, progress + Window.PROGRESS_START); +    } + +    @Override +    public void setSecondaryProgress(int secondaryProgress) { +        if (DEBUG) Log.d(TAG, "[setSecondaryProgress] secondaryProgress: " + secondaryProgress); + +        setFeatureInt(Window.FEATURE_PROGRESS, +                secondaryProgress + Window.PROGRESS_SECONDARY_START); +    } + +    private void setFeatureInt(int featureId, int value) { +        updateInt(featureId, value, false); +    } + +    private void updateInt(int featureId, int value, boolean fromResume) { +        // Do nothing if the decor is not yet installed... an update will +        // need to be forced when we eventually become active. +        if (mContentParent == null) { +            return; +        } + +        final int featureMask = 1 << featureId; + +        if ((getFeatures() & featureMask) == 0 && !fromResume) { +            return; +        } + +        onIntChanged(featureId, value); +    } + +    private void onIntChanged(int featureId, int value) { +        if (featureId == Window.FEATURE_PROGRESS || featureId == Window.FEATURE_INDETERMINATE_PROGRESS) { +            updateProgressBars(value); +        } +    } + +    private void updateProgressBars(int value) { +        IcsProgressBar circularProgressBar = getCircularProgressBar(true); +        IcsProgressBar horizontalProgressBar = getHorizontalProgressBar(true); + +        final int features = mFeatures;//getLocalFeatures(); +        if (value == Window.PROGRESS_VISIBILITY_ON) { +            if ((features & (1 << Window.FEATURE_PROGRESS)) != 0) { +                int level = horizontalProgressBar.getProgress(); +                int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ? +                        View.VISIBLE : View.INVISIBLE; +                horizontalProgressBar.setVisibility(visibility); +            } +            if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0) { +                circularProgressBar.setVisibility(View.VISIBLE); +            } +        } else if (value == Window.PROGRESS_VISIBILITY_OFF) { +            if ((features & (1 << Window.FEATURE_PROGRESS)) != 0) { +                horizontalProgressBar.setVisibility(View.GONE); +            } +            if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0) { +                circularProgressBar.setVisibility(View.GONE); +            } +        } else if (value == Window.PROGRESS_INDETERMINATE_ON) { +            horizontalProgressBar.setIndeterminate(true); +        } else if (value == Window.PROGRESS_INDETERMINATE_OFF) { +            horizontalProgressBar.setIndeterminate(false); +        } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) { +            // We want to set the progress value before testing for visibility +            // so that when the progress bar becomes visible again, it has the +            // correct level. +            horizontalProgressBar.setProgress(value - Window.PROGRESS_START); + +            if (value < Window.PROGRESS_END) { +                showProgressBars(horizontalProgressBar, circularProgressBar); +            } else { +                hideProgressBars(horizontalProgressBar, circularProgressBar); +            } +        } else if (Window.PROGRESS_SECONDARY_START <= value && value <= Window.PROGRESS_SECONDARY_END) { +            horizontalProgressBar.setSecondaryProgress(value - Window.PROGRESS_SECONDARY_START); + +            showProgressBars(horizontalProgressBar, circularProgressBar); +        } +    } + +    private void showProgressBars(IcsProgressBar horizontalProgressBar, IcsProgressBar spinnyProgressBar) { +        final int features = mFeatures;//getLocalFeatures(); +        if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0 && +                spinnyProgressBar.getVisibility() == View.INVISIBLE) { +            spinnyProgressBar.setVisibility(View.VISIBLE); +        } +        // Only show the progress bars if the primary progress is not complete +        if ((features & (1 << Window.FEATURE_PROGRESS)) != 0 && +                horizontalProgressBar.getProgress() < 10000) { +            horizontalProgressBar.setVisibility(View.VISIBLE); +        } +    } + +    private void hideProgressBars(IcsProgressBar horizontalProgressBar, IcsProgressBar spinnyProgressBar) { +        final int features = mFeatures;//getLocalFeatures(); +        Animation anim = AnimationUtils.loadAnimation(mActivity, android.R.anim.fade_out); +        anim.setDuration(1000); +        if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0 && +                spinnyProgressBar.getVisibility() == View.VISIBLE) { +            spinnyProgressBar.startAnimation(anim); +            spinnyProgressBar.setVisibility(View.INVISIBLE); +        } +        if ((features & (1 << Window.FEATURE_PROGRESS)) != 0 && +                horizontalProgressBar.getVisibility() == View.VISIBLE) { +            horizontalProgressBar.startAnimation(anim); +            horizontalProgressBar.setVisibility(View.INVISIBLE); +        } +    } + +    private IcsProgressBar getCircularProgressBar(boolean shouldInstallDecor) { +        if (mCircularProgressBar != null) { +            return mCircularProgressBar; +        } +        if (mContentParent == null && shouldInstallDecor) { +            installDecor(); +        } +        mCircularProgressBar = (IcsProgressBar)mDecor.findViewById(R.id.abs__progress_circular); +        if (mCircularProgressBar != null) { +            mCircularProgressBar.setVisibility(View.INVISIBLE); +        } +        return mCircularProgressBar; +    } + +    private IcsProgressBar getHorizontalProgressBar(boolean shouldInstallDecor) { +        if (mHorizontalProgressBar != null) { +            return mHorizontalProgressBar; +        } +        if (mContentParent == null && shouldInstallDecor) { +            installDecor(); +        } +        mHorizontalProgressBar = (IcsProgressBar)mDecor.findViewById(R.id.abs__progress_horizontal); +        if (mHorizontalProgressBar != null) { +            mHorizontalProgressBar.setVisibility(View.INVISIBLE); +        } +        return mHorizontalProgressBar; +    } + + +    /////////////////////////////////////////////////////////////////////////// +    // Feature management and content interaction and creation +    /////////////////////////////////////////////////////////////////////////// + +    private int getFeatures() { +        if (DEBUG) Log.d(TAG, "[getFeatures] returning " + mFeatures); + +        return mFeatures; +    } + +    @Override +    public boolean hasFeature(int featureId) { +        if (DEBUG) Log.d(TAG, "[hasFeature] featureId: " + featureId); + +        boolean result = (mFeatures & (1 << featureId)) != 0; +        if (DEBUG) Log.d(TAG, "[hasFeature] returning " + result); +        return result; +    } + +    @Override +    public boolean requestFeature(int featureId) { +        if (DEBUG) Log.d(TAG, "[requestFeature] featureId: " + featureId); + +        if (mContentParent != null) { +            throw new AndroidRuntimeException("requestFeature() must be called before adding content"); +        } + +        switch (featureId) { +            case Window.FEATURE_ACTION_BAR: +            case Window.FEATURE_ACTION_BAR_OVERLAY: +            case Window.FEATURE_ACTION_MODE_OVERLAY: +            case Window.FEATURE_INDETERMINATE_PROGRESS: +            case Window.FEATURE_NO_TITLE: +            case Window.FEATURE_PROGRESS: +                mFeatures |= (1 << featureId); +                return true; + +            default: +                return false; +        } +    } + +    @Override +    public void setUiOptions(int uiOptions) { +        if (DEBUG) Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions); + +        mUiOptions = uiOptions; +    } + +    @Override +    public void setUiOptions(int uiOptions, int mask) { +        if (DEBUG) Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions + ", mask: " + mask); + +        mUiOptions = (mUiOptions & ~mask) | (uiOptions & mask); +    } + +    @Override +    public void setContentView(int layoutResId) { +        if (DEBUG) Log.d(TAG, "[setContentView] layoutResId: " + layoutResId); + +        if (mContentParent == null) { +            installDecor(); +        } else { +            mContentParent.removeAllViews(); +        } +        mActivity.getLayoutInflater().inflate(layoutResId, mContentParent); + +        android.view.Window.Callback callback = mActivity.getWindow().getCallback(); +        if (callback != null) { +            callback.onContentChanged(); +        } + +        initActionBar(); +    } + +    @Override +    public void setContentView(View view, ViewGroup.LayoutParams params) { +        if (DEBUG) Log.d(TAG, "[setContentView] view: " + view + ", params: " + params); + +        if (mContentParent == null) { +            installDecor(); +        } else { +            mContentParent.removeAllViews(); +        } +        mContentParent.addView(view, params); + +        android.view.Window.Callback callback = mActivity.getWindow().getCallback(); +        if (callback != null) { +            callback.onContentChanged(); +        } + +        initActionBar(); +    } + +    @Override +    public void addContentView(View view, ViewGroup.LayoutParams params) { +        if (DEBUG) Log.d(TAG, "[addContentView] view: " + view + ", params: " + params); + +        if (mContentParent == null) { +            installDecor(); +        } +        mContentParent.addView(view, params); + +        initActionBar(); +    } + +    private void installDecor() { +        if (DEBUG) Log.d(TAG, "[installDecor]"); + +        if (mDecor == null) { +            mDecor = (ViewGroup)mActivity.getWindow().getDecorView().findViewById(android.R.id.content); +        } +        if (mContentParent == null) { +            //Since we are not operating at the window level we need to take +            //into account the fact that the true decor may have already been +            //initialized and had content attached to it. If that is the case, +            //copy over its children to our new content container. +            List<View> views = null; +            if (mDecor.getChildCount() > 0) { +                views = new ArrayList<View>(1); //Usually there's only one child +                for (int i = 0, children = mDecor.getChildCount(); i < children; i++) { +                    View child = mDecor.getChildAt(0); +                    mDecor.removeView(child); +                    views.add(child); +                } +            } + +            mContentParent = generateLayout(); + +            //Copy over the old children. See above for explanation. +            if (views != null) { +                for (View child : views) { +                    mContentParent.addView(child); +                } +            } + +            mTitleView = (TextView)mDecor.findViewById(android.R.id.title); +            if (mTitleView != null) { +                if (hasFeature(Window.FEATURE_NO_TITLE)) { +                    mTitleView.setVisibility(View.GONE); +                    if (mContentParent instanceof FrameLayout) { +                        ((FrameLayout)mContentParent).setForeground(null); +                    } +                } else { +                    mTitleView.setText(mTitle); +                } +            } else { +                wActionBar = (ActionBarView)mDecor.findViewById(R.id.abs__action_bar); +                if (wActionBar != null) { +                    wActionBar.setWindowCallback(this); +                    if (wActionBar.getTitle() == null) { +                        wActionBar.setWindowTitle(mActivity.getTitle()); +                    } +                    if (hasFeature(Window.FEATURE_PROGRESS)) { +                        wActionBar.initProgress(); +                    } +                    if (hasFeature(Window.FEATURE_INDETERMINATE_PROGRESS)) { +                        wActionBar.initIndeterminateProgress(); +                    } + +                    //Since we don't require onCreate dispatching, parse for uiOptions here +                    int uiOptions = loadUiOptionsFromManifest(mActivity); +                    if (uiOptions != 0) { +                        mUiOptions = uiOptions; +                    } + +                    boolean splitActionBar = false; +                    final boolean splitWhenNarrow = (mUiOptions & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0; +                    if (splitWhenNarrow) { +                        splitActionBar = getResources_getBoolean(mActivity, R.bool.abs__split_action_bar_is_narrow); +                    } else { +                        splitActionBar = mActivity.getTheme() +                                .obtainStyledAttributes(R.styleable.SherlockTheme) +                                .getBoolean(R.styleable.SherlockTheme_windowSplitActionBar, false); +                    } +                    final ActionBarContainer splitView = (ActionBarContainer)mDecor.findViewById(R.id.abs__split_action_bar); +                    if (splitView != null) { +                        wActionBar.setSplitView(splitView); +                        wActionBar.setSplitActionBar(splitActionBar); +                        wActionBar.setSplitWhenNarrow(splitWhenNarrow); + +                        mActionModeView = (ActionBarContextView)mDecor.findViewById(R.id.abs__action_context_bar); +                        mActionModeView.setSplitView(splitView); +                        mActionModeView.setSplitActionBar(splitActionBar); +                        mActionModeView.setSplitWhenNarrow(splitWhenNarrow); +                    } else if (splitActionBar) { +                        Log.e(TAG, "Requested split action bar with incompatible window decor! Ignoring request."); +                    } + +                    // Post the panel invalidate for later; avoid application onCreateOptionsMenu +                    // being called in the middle of onCreate or similar. +                    mDecor.post(new Runnable() { +                        @Override +                        public void run() { +                            //Invalidate if the panel menu hasn't been created before this. +                            if (!mIsDestroyed && !mActivity.isFinishing() && mMenu == null) { +                                dispatchInvalidateOptionsMenu(); +                            } +                        } +                    }); +                } +            } +        } +    } + +    private ViewGroup generateLayout() { +        if (DEBUG) Log.d(TAG, "[generateLayout]"); + +        // Apply data from current theme. + +        TypedArray a = mActivity.getTheme().obtainStyledAttributes(R.styleable.SherlockTheme); + +        mIsFloating = a.getBoolean(R.styleable.SherlockTheme_android_windowIsFloating, false); + +        if (!a.hasValue(R.styleable.SherlockTheme_windowActionBar)) { +            throw new IllegalStateException("You must use Theme.Sherlock, Theme.Sherlock.Light, Theme.Sherlock.Light.DarkActionBar, or a derivative."); +        } + +        if (a.getBoolean(R.styleable.SherlockTheme_windowNoTitle, false)) { +            requestFeature(Window.FEATURE_NO_TITLE); +        } else if (a.getBoolean(R.styleable.SherlockTheme_windowActionBar, false)) { +            // Don't allow an action bar if there is no title. +            requestFeature(Window.FEATURE_ACTION_BAR); +        } + +        if (a.getBoolean(R.styleable.SherlockTheme_windowActionBarOverlay, false)) { +            requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY); +        } + +        if (a.getBoolean(R.styleable.SherlockTheme_windowActionModeOverlay, false)) { +            requestFeature(Window.FEATURE_ACTION_MODE_OVERLAY); +        } + +        a.recycle(); + +        int layoutResource; +        if (!hasFeature(Window.FEATURE_NO_TITLE)) { +            if (mIsFloating) { +                //Trash original dialog LinearLayout +                mDecor = (ViewGroup)mDecor.getParent(); +                mDecor.removeAllViews(); + +                layoutResource = R.layout.abs__dialog_title_holo; +            } else { +                if (hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY)) { +                    layoutResource = R.layout.abs__screen_action_bar_overlay; +                } else { +                    layoutResource = R.layout.abs__screen_action_bar; +                } +            } +        } else if (hasFeature(Window.FEATURE_ACTION_MODE_OVERLAY) && !hasFeature(Window.FEATURE_NO_TITLE)) { +            layoutResource = R.layout.abs__screen_simple_overlay_action_mode; +        } else { +            layoutResource = R.layout.abs__screen_simple; +        } + +        if (DEBUG) Log.d(TAG, "[generateLayout] using screen XML " + mActivity.getResources().getString(layoutResource)); +        View in = mActivity.getLayoutInflater().inflate(layoutResource, null); +        mDecor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + +        ViewGroup contentParent = (ViewGroup)mDecor.findViewById(R.id.abs__content); +        if (contentParent == null) { +            throw new RuntimeException("Couldn't find content container view"); +        } + +        //Make our new child the true content view (for fragments). VERY VOLATILE! +        mDecor.setId(View.NO_ID); +        contentParent.setId(android.R.id.content); + +        if (hasFeature(Window.FEATURE_INDETERMINATE_PROGRESS)) { +            IcsProgressBar progress = getCircularProgressBar(false); +            if (progress != null) { +                progress.setIndeterminate(true); +            } +        } + +        return contentParent; +    } + + +    /////////////////////////////////////////////////////////////////////////// +    // Miscellaneous +    /////////////////////////////////////////////////////////////////////////// + +    /** +     * Determine whether or not the device has a dedicated menu key. +     * +     * @return {@code true} if native menu key is present. +     */ +    private boolean isReservingOverflow() { +        if (!mReserveOverflowSet) { +            mReserveOverflow = ActionMenuPresenter.reserveOverflow(mActivity); +            mReserveOverflowSet = true; +        } +        return mReserveOverflow; +    } + +    private static int loadUiOptionsFromManifest(Activity activity) { +        int uiOptions = 0; +        try { +            final String thisPackage = activity.getClass().getName(); +            if (DEBUG) Log.i(TAG, "Parsing AndroidManifest.xml for " + thisPackage); + +            final String packageName = activity.getApplicationInfo().packageName; +            final AssetManager am = activity.createPackageContext(packageName, 0).getAssets(); +            final XmlResourceParser xml = am.openXmlResourceParser("AndroidManifest.xml"); + +            int eventType = xml.getEventType(); +            while (eventType != XmlPullParser.END_DOCUMENT) { +                if (eventType == XmlPullParser.START_TAG) { +                    String name = xml.getName(); + +                    if ("application".equals(name)) { +                        //Check if the <application> has the attribute +                        if (DEBUG) Log.d(TAG, "Got <application>"); + +                        for (int i = xml.getAttributeCount() - 1; i >= 0; i--) { +                            if (DEBUG) Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i)); + +                            if ("uiOptions".equals(xml.getAttributeName(i))) { +                                uiOptions = xml.getAttributeIntValue(i, 0); +                                break; //out of for loop +                            } +                        } +                    } else if ("activity".equals(name)) { +                        //Check if the <activity> is us and has the attribute +                        if (DEBUG) Log.d(TAG, "Got <activity>"); +                        Integer activityUiOptions = null; +                        String activityPackage = null; +                        boolean isOurActivity = false; + +                        for (int i = xml.getAttributeCount() - 1; i >= 0; i--) { +                            if (DEBUG) Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i)); + +                            //We need both uiOptions and name attributes +                            String attrName = xml.getAttributeName(i); +                            if ("uiOptions".equals(attrName)) { +                                activityUiOptions = xml.getAttributeIntValue(i, 0); +                            } else if ("name".equals(attrName)) { +                                activityPackage = cleanActivityName(packageName, xml.getAttributeValue(i)); +                                if (!thisPackage.equals(activityPackage)) { +                                    break; //out of for loop +                                } +                                isOurActivity = true; +                            } + +                            //Make sure we have both attributes before processing +                            if ((activityUiOptions != null) && (activityPackage != null)) { +                                //Our activity, uiOptions specified, override with our value +                                uiOptions = activityUiOptions.intValue(); +                            } +                        } +                        if (isOurActivity) { +                            //If we matched our activity but it had no logo don't +                            //do any more processing of the manifest +                            break; +                        } +                    } +                } +                eventType = xml.nextToken(); +            } +        } catch (Exception e) { +            e.printStackTrace(); +        } +        if (DEBUG) Log.i(TAG, "Returning " + Integer.toHexString(uiOptions)); +        return uiOptions; +    } + +    public static String cleanActivityName(String manifestPackage, String activityName) { +        if (activityName.charAt(0) == '.') { +            //Relative activity name (e.g., android:name=".ui.SomeClass") +            return manifestPackage + activityName; +        } +        if (activityName.indexOf('.', 1) == -1) { +            //Unqualified activity name (e.g., android:name="SomeClass") +            return manifestPackage + "." + activityName; +        } +        //Fully-qualified activity name (e.g., "com.my.package.SomeClass") +        return activityName; +    } + +    /** +     * Clears out internal reference when the action mode is destroyed. +     */ +    private class ActionModeCallbackWrapper implements ActionMode.Callback { +        private final ActionMode.Callback mWrapped; + +        public ActionModeCallbackWrapper(ActionMode.Callback wrapped) { +            mWrapped = wrapped; +        } + +        public boolean onCreateActionMode(ActionMode mode, Menu menu) { +            return mWrapped.onCreateActionMode(mode, menu); +        } + +        public boolean onPrepareActionMode(ActionMode mode, Menu menu) { +            return mWrapped.onPrepareActionMode(mode, menu); +        } + +        public boolean onActionItemClicked(ActionMode mode, MenuItem item) { +            return mWrapped.onActionItemClicked(mode, item); +        } + +        public void onDestroyActionMode(ActionMode mode) { +            mWrapped.onDestroyActionMode(mode); +            if (mActionModeView != null) { +                mActionModeView.setVisibility(View.GONE); +                mActionModeView.removeAllViews(); +            } +            if (mActivity instanceof OnActionModeFinishedListener) { +                ((OnActionModeFinishedListener)mActivity).onActionModeFinished(mActionMode); +            } +            mActionMode = null; +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockNative.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockNative.java new file mode 100644 index 000000000..0824d3848 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockNative.java @@ -0,0 +1,336 @@ +package com.actionbarsherlock.internal; + +import com.actionbarsherlock.ActionBarSherlock; +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.internal.app.ActionBarWrapper; +import com.actionbarsherlock.internal.view.menu.MenuWrapper; +import com.actionbarsherlock.view.ActionMode; +import com.actionbarsherlock.view.MenuInflater; +import android.app.Activity; +import android.content.Context; +import android.util.Log; +import android.util.TypedValue; +import android.view.ContextThemeWrapper; +import android.view.View; +import android.view.Window; +import android.view.ViewGroup.LayoutParams; + +@ActionBarSherlock.Implementation(api = 14) +public class ActionBarSherlockNative extends ActionBarSherlock { +    private ActionBarWrapper mActionBar; +    private ActionModeWrapper mActionMode; +    private MenuWrapper mMenu; + +    public ActionBarSherlockNative(Activity activity, int flags) { +        super(activity, flags); +    } + + +    @Override +    public ActionBar getActionBar() { +        if (DEBUG) Log.d(TAG, "[getActionBar]"); + +        initActionBar(); +        return mActionBar; +    } + +    private void initActionBar() { +        if (mActionBar != null || mActivity.getActionBar() == null) { +            return; +        } + +        mActionBar = new ActionBarWrapper(mActivity); +    } + +    @Override +    public void dispatchInvalidateOptionsMenu() { +        if (DEBUG) Log.d(TAG, "[dispatchInvalidateOptionsMenu]"); + +        mActivity.getWindow().invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); +    } + +    @Override +    public boolean dispatchCreateOptionsMenu(android.view.Menu menu) { +        if (DEBUG) Log.d(TAG, "[dispatchCreateOptionsMenu] menu: " + menu); + +        if (mMenu == null || menu != mMenu.unwrap()) { +            mMenu = new MenuWrapper(menu); +        } + +        final boolean result = callbackCreateOptionsMenu(mMenu); +        if (DEBUG) Log.d(TAG, "[dispatchCreateOptionsMenu] returning " + result); +        return result; +    } + +    @Override +    public boolean dispatchPrepareOptionsMenu(android.view.Menu menu) { +        if (DEBUG) Log.d(TAG, "[dispatchPrepareOptionsMenu] menu: " + menu); + +        final boolean result = callbackPrepareOptionsMenu(mMenu); +        if (DEBUG) Log.d(TAG, "[dispatchPrepareOptionsMenu] returning " + result); +        return result; +    } + +    @Override +    public boolean dispatchOptionsItemSelected(android.view.MenuItem item) { +        if (DEBUG) Log.d(TAG, "[dispatchOptionsItemSelected] item: " + item.getTitleCondensed()); + +        final boolean result = callbackOptionsItemSelected(mMenu.findItem(item)); +        if (DEBUG) Log.d(TAG, "[dispatchOptionsItemSelected] returning " + result); +        return result; +    } + +    @Override +    public boolean hasFeature(int feature) { +        if (DEBUG) Log.d(TAG, "[hasFeature] feature: " + feature); + +        final boolean result = mActivity.getWindow().hasFeature(feature); +        if (DEBUG) Log.d(TAG, "[hasFeature] returning " + result); +        return result; +    } + +    @Override +    public boolean requestFeature(int featureId) { +        if (DEBUG) Log.d(TAG, "[requestFeature] featureId: " + featureId); + +        final boolean result = mActivity.getWindow().requestFeature(featureId); +        if (DEBUG) Log.d(TAG, "[requestFeature] returning " + result); +        return result; +    } + +    @Override +    public void setUiOptions(int uiOptions) { +        if (DEBUG) Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions); + +        mActivity.getWindow().setUiOptions(uiOptions); +    } + +    @Override +    public void setUiOptions(int uiOptions, int mask) { +        if (DEBUG) Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions + ", mask: " + mask); + +        mActivity.getWindow().setUiOptions(uiOptions, mask); +    } + +    @Override +    public void setContentView(int layoutResId) { +        if (DEBUG) Log.d(TAG, "[setContentView] layoutResId: " + layoutResId); + +        mActivity.getWindow().setContentView(layoutResId); +        initActionBar(); +    } + +    @Override +    public void setContentView(View view, LayoutParams params) { +        if (DEBUG) Log.d(TAG, "[setContentView] view: " + view + ", params: " + params); + +        mActivity.getWindow().setContentView(view, params); +        initActionBar(); +    } + +    @Override +    public void addContentView(View view, LayoutParams params) { +        if (DEBUG) Log.d(TAG, "[addContentView] view: " + view + ", params: " + params); + +        mActivity.getWindow().addContentView(view, params); +        initActionBar(); +    } + +    @Override +    public void setTitle(CharSequence title) { +        if (DEBUG) Log.d(TAG, "[setTitle] title: " + title); + +        mActivity.getWindow().setTitle(title); +    } + +    @Override +    public void setProgressBarVisibility(boolean visible) { +        if (DEBUG) Log.d(TAG, "[setProgressBarVisibility] visible: " + visible); + +        mActivity.setProgressBarVisibility(visible); +    } + +    @Override +    public void setProgressBarIndeterminateVisibility(boolean visible) { +        if (DEBUG) Log.d(TAG, "[setProgressBarIndeterminateVisibility] visible: " + visible); + +        mActivity.setProgressBarIndeterminateVisibility(visible); +    } + +    @Override +    public void setProgressBarIndeterminate(boolean indeterminate) { +        if (DEBUG) Log.d(TAG, "[setProgressBarIndeterminate] indeterminate: " + indeterminate); + +        mActivity.setProgressBarIndeterminate(indeterminate); +    } + +    @Override +    public void setProgress(int progress) { +        if (DEBUG) Log.d(TAG, "[setProgress] progress: " + progress); + +        mActivity.setProgress(progress); +    } + +    @Override +    public void setSecondaryProgress(int secondaryProgress) { +        if (DEBUG) Log.d(TAG, "[setSecondaryProgress] secondaryProgress: " + secondaryProgress); + +        mActivity.setSecondaryProgress(secondaryProgress); +    } + +    @Override +    protected Context getThemedContext() { +        Context context = mActivity; +        TypedValue outValue = new TypedValue(); +        mActivity.getTheme().resolveAttribute(android.R.attr.actionBarWidgetTheme, outValue, true); +        if (outValue.resourceId != 0) { +            //We are unable to test if this is the same as our current theme +            //so we just wrap it and hope that if the attribute was specified +            //then the user is intentionally specifying an alternate theme. +            context = new ContextThemeWrapper(context, outValue.resourceId); +        } +        return context; +    } + +    @Override +    public ActionMode startActionMode(com.actionbarsherlock.view.ActionMode.Callback callback) { +        if (DEBUG) Log.d(TAG, "[startActionMode] callback: " + callback); + +        if (mActionMode != null) { +            mActionMode.finish(); +        } +        ActionModeCallbackWrapper wrapped = null; +        if (callback != null) { +            wrapped = new ActionModeCallbackWrapper(callback); +        } + +        //Calling this will trigger the callback wrapper's onCreate which +        //is where we will set the new instance to mActionMode since we need +        //to pass it through to the sherlock callbacks and the call below +        //will not have returned yet to store its value. +        if (mActivity.startActionMode(wrapped) == null) { +            mActionMode = null; +        } +        if (mActivity instanceof OnActionModeStartedListener && mActionMode != null) { +            ((OnActionModeStartedListener)mActivity).onActionModeStarted(mActionMode); +        } + +        return mActionMode; +    } + +    private class ActionModeCallbackWrapper implements android.view.ActionMode.Callback { +        private final ActionMode.Callback mCallback; + +        public ActionModeCallbackWrapper(ActionMode.Callback callback) { +            mCallback = callback; +        } + +        @Override +        public boolean onCreateActionMode(android.view.ActionMode mode, android.view.Menu menu) { +            //See ActionBarSherlockNative#startActionMode +            mActionMode = new ActionModeWrapper(mode); + +            return mCallback.onCreateActionMode(mActionMode, mActionMode.getMenu()); +        } + +        @Override +        public boolean onPrepareActionMode(android.view.ActionMode mode, android.view.Menu menu) { +            return mCallback.onPrepareActionMode(mActionMode, mActionMode.getMenu()); +        } + +        @Override +        public boolean onActionItemClicked(android.view.ActionMode mode, android.view.MenuItem item) { +            return mCallback.onActionItemClicked(mActionMode, mActionMode.getMenu().findItem(item)); +        } + +        @Override +        public void onDestroyActionMode(android.view.ActionMode mode) { +            mCallback.onDestroyActionMode(mActionMode); +            if (mActivity instanceof OnActionModeFinishedListener) { +                ((OnActionModeFinishedListener)mActivity).onActionModeFinished(mActionMode); +            } +        } +    } + +    private class ActionModeWrapper extends ActionMode { +        private final android.view.ActionMode mActionMode; +        private MenuWrapper mMenu = null; + +        ActionModeWrapper(android.view.ActionMode actionMode) { +            mActionMode = actionMode; +        } + +        @Override +        public void setTitle(CharSequence title) { +            mActionMode.setTitle(title); +        } + +        @Override +        public void setTitle(int resId) { +            mActionMode.setTitle(resId); +        } + +        @Override +        public void setSubtitle(CharSequence subtitle) { +            mActionMode.setSubtitle(subtitle); +        } + +        @Override +        public void setSubtitle(int resId) { +            mActionMode.setSubtitle(resId); +        } + +        @Override +        public void setCustomView(View view) { +            mActionMode.setCustomView(view); +        } + +        @Override +        public void invalidate() { +            mActionMode.invalidate(); +        } + +        @Override +        public void finish() { +            mActionMode.finish(); +        } + +        @Override +        public MenuWrapper getMenu() { +            if (mMenu == null) { +                mMenu = new MenuWrapper(mActionMode.getMenu()); +            } +            return mMenu; +        } + +        @Override +        public CharSequence getTitle() { +            return mActionMode.getTitle(); +        } + +        @Override +        public CharSequence getSubtitle() { +            return mActionMode.getSubtitle(); +        } + +        @Override +        public View getCustomView() { +            return mActionMode.getCustomView(); +        } + +        @Override +        public MenuInflater getMenuInflater() { +            return ActionBarSherlockNative.this.getMenuInflater(); +        } + +        @Override +        public void setTag(Object tag) { +            mActionMode.setTag(tag); +        } + +        @Override +        public Object getTag() { +            return mActionMode.getTag(); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/ResourcesCompat.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/ResourcesCompat.java new file mode 100644 index 000000000..8e1efe8c5 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/ResourcesCompat.java @@ -0,0 +1,95 @@ +package com.actionbarsherlock.internal; + +import android.content.Context; +import android.os.Build; +import android.util.DisplayMetrics; +import com.actionbarsherlock.R; + +public final class ResourcesCompat { +    //No instances +    private ResourcesCompat() {} + + +    /** +     * Support implementation of {@code getResources().getBoolean()} that we +     * can use to simulate filtering based on width and smallest width +     * qualifiers on pre-3.2. +     * +     * @param context Context to load booleans from on 3.2+ and to fetch the +     * display metrics. +     * @param id Id of boolean to load. +     * @return Associated boolean value as reflected by the current display +     * metrics. +     */ +    public static boolean getResources_getBoolean(Context context, int id) { +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { +            return context.getResources().getBoolean(id); +        } + +        DisplayMetrics metrics = context.getResources().getDisplayMetrics(); +        float widthDp = metrics.widthPixels / metrics.density; +        float heightDp = metrics.heightPixels / metrics.density; +        float smallestWidthDp = (widthDp < heightDp) ? widthDp : heightDp; + +        if (id == R.bool.abs__action_bar_embed_tabs) { +            if (widthDp >= 480) { +                return true; //values-w480dp +            } +            return false; //values +        } +        if (id == R.bool.abs__split_action_bar_is_narrow) { +            if (widthDp >= 480) { +                return false; //values-w480dp +            } +            return true; //values +        } +        if (id == R.bool.abs__action_bar_expanded_action_views_exclusive) { +            if (smallestWidthDp >= 600) { +                return false; //values-sw600dp +            } +            return true; //values +        } +        if (id == R.bool.abs__config_allowActionMenuItemTextWithIcon) { +            if (widthDp >= 480) { +                return true; //values-w480dp +            } +            return false; //values +        } + +        throw new IllegalArgumentException("Unknown boolean resource ID " + id); +    } + +    /** +     * Support implementation of {@code getResources().getInteger()} that we +     * can use to simulate filtering based on width qualifiers on pre-3.2. +     * +     * @param context Context to load integers from on 3.2+ and to fetch the +     * display metrics. +     * @param id Id of integer to load. +     * @return Associated integer value as reflected by the current display +     * metrics. +     */ +    public static int getResources_getInteger(Context context, int id) { +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { +            return context.getResources().getInteger(id); +        } + +        DisplayMetrics metrics = context.getResources().getDisplayMetrics(); +        float widthDp = metrics.widthPixels / metrics.density; + +        if (id == R.integer.abs__max_action_buttons) { +            if (widthDp >= 600) { +                return 5; //values-w600dp +            } +            if (widthDp >= 500) { +                return 4; //values-w500dp +            } +            if (widthDp >= 360) { +                return 3; //values-w360dp +            } +            return 2; //values +        } + +        throw new IllegalArgumentException("Unknown integer resource ID " + id); +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarImpl.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarImpl.java new file mode 100644 index 000000000..d022a2465 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarImpl.java @@ -0,0 +1,1026 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.app; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Handler; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.util.TypedValue; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.Window; +import android.view.accessibility.AccessibilityEvent; +import android.widget.SpinnerAdapter; +import com.actionbarsherlock.R; +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.internal.nineoldandroids.animation.Animator; +import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorListenerAdapter; +import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorSet; +import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator; +import com.actionbarsherlock.internal.nineoldandroids.animation.Animator.AnimatorListener; +import com.actionbarsherlock.internal.nineoldandroids.widget.NineFrameLayout; +import com.actionbarsherlock.internal.view.menu.MenuBuilder; +import com.actionbarsherlock.internal.view.menu.MenuPopupHelper; +import com.actionbarsherlock.internal.view.menu.SubMenuBuilder; +import com.actionbarsherlock.internal.widget.ActionBarContainer; +import com.actionbarsherlock.internal.widget.ActionBarContextView; +import com.actionbarsherlock.internal.widget.ActionBarView; +import com.actionbarsherlock.internal.widget.ScrollingTabContainerView; +import com.actionbarsherlock.view.ActionMode; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuInflater; +import com.actionbarsherlock.view.MenuItem; +import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean; + +/** + * ActionBarImpl is the ActionBar implementation used + * by devices of all screen sizes. If it detects a compatible decor, + * it will split contextual modes across both the ActionBarView at + * the top of the screen and a horizontal LinearLayout at the bottom + * which is normally hidden. + */ +public class ActionBarImpl extends ActionBar { +    //UNUSED private static final String TAG = "ActionBarImpl"; + +    private Context mContext; +    private Context mThemedContext; +    private Activity mActivity; +    //UNUSED private Dialog mDialog; + +    private ActionBarContainer mContainerView; +    private ActionBarView mActionView; +    private ActionBarContextView mContextView; +    private ActionBarContainer mSplitView; +    private NineFrameLayout mContentView; +    private ScrollingTabContainerView mTabScrollView; + +    private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>(); + +    private TabImpl mSelectedTab; +    private int mSavedTabPosition = INVALID_POSITION; + +    ActionModeImpl mActionMode; +    ActionMode mDeferredDestroyActionMode; +    ActionMode.Callback mDeferredModeDestroyCallback; + +    private boolean mLastMenuVisibility; +    private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners = +            new ArrayList<OnMenuVisibilityListener>(); + +    private static final int CONTEXT_DISPLAY_NORMAL = 0; +    private static final int CONTEXT_DISPLAY_SPLIT = 1; + +    private static final int INVALID_POSITION = -1; + +    private int mContextDisplayMode; +    private boolean mHasEmbeddedTabs; + +    final Handler mHandler = new Handler(); +    Runnable mTabSelector; + +    private Animator mCurrentShowAnim; +    private Animator mCurrentModeAnim; +    private boolean mShowHideAnimationEnabled; +    boolean mWasHiddenBeforeMode; + +    final AnimatorListener mHideListener = new AnimatorListenerAdapter() { +        @Override +        public void onAnimationEnd(Animator animation) { +            if (mContentView != null) { +                mContentView.setTranslationY(0); +                mContainerView.setTranslationY(0); +            } +            if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) { +                mSplitView.setVisibility(View.GONE); +            } +            mContainerView.setVisibility(View.GONE); +            mContainerView.setTransitioning(false); +            mCurrentShowAnim = null; +            completeDeferredDestroyActionMode(); +        } +    }; + +    final AnimatorListener mShowListener = new AnimatorListenerAdapter() { +        @Override +        public void onAnimationEnd(Animator animation) { +            mCurrentShowAnim = null; +            mContainerView.requestLayout(); +        } +    }; + +    public ActionBarImpl(Activity activity, int features) { +        mActivity = activity; +        Window window = activity.getWindow(); +        View decor = window.getDecorView(); +        init(decor); + +        //window.hasFeature() workaround for pre-3.0 +        if ((features & (1 << Window.FEATURE_ACTION_BAR_OVERLAY)) == 0) { +            mContentView = (NineFrameLayout)decor.findViewById(android.R.id.content); +        } +    } + +    public ActionBarImpl(Dialog dialog) { +        //UNUSED mDialog = dialog; +        init(dialog.getWindow().getDecorView()); +    } + +    private void init(View decor) { +        mContext = decor.getContext(); +        mActionView = (ActionBarView) decor.findViewById(R.id.abs__action_bar); +        mContextView = (ActionBarContextView) decor.findViewById( +                R.id.abs__action_context_bar); +        mContainerView = (ActionBarContainer) decor.findViewById( +                R.id.abs__action_bar_container); +        mSplitView = (ActionBarContainer) decor.findViewById( +                R.id.abs__split_action_bar); + +        if (mActionView == null || mContextView == null || mContainerView == null) { +            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + +                    "with a compatible window decor layout"); +        } + +        mActionView.setContextView(mContextView); +        mContextDisplayMode = mActionView.isSplitActionBar() ? +                CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL; + +        // Older apps get the home button interaction enabled by default. +        // Newer apps need to enable it explicitly. +        setHomeButtonEnabled(mContext.getApplicationInfo().targetSdkVersion < 14); + +        setHasEmbeddedTabs(getResources_getBoolean(mContext, +                R.bool.abs__action_bar_embed_tabs)); +    } + +    public void onConfigurationChanged(Configuration newConfig) { +        setHasEmbeddedTabs(getResources_getBoolean(mContext, +                R.bool.abs__action_bar_embed_tabs)); + +        //Manually dispatch a configuration change to the action bar view on pre-2.2 +        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) { +            mActionView.onConfigurationChanged(newConfig); +            if (mContextView != null) { +                mContextView.onConfigurationChanged(newConfig); +            } +        } +    } + +    private void setHasEmbeddedTabs(boolean hasEmbeddedTabs) { +        mHasEmbeddedTabs = hasEmbeddedTabs; +        // Switch tab layout configuration if needed +        if (!mHasEmbeddedTabs) { +            mActionView.setEmbeddedTabView(null); +            mContainerView.setTabContainer(mTabScrollView); +        } else { +            mContainerView.setTabContainer(null); +            mActionView.setEmbeddedTabView(mTabScrollView); +        } +        final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS; +        if (mTabScrollView != null) { +            mTabScrollView.setVisibility(isInTabMode ? View.VISIBLE : View.GONE); +        } +        mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode); +    } + +    private void ensureTabsExist() { +        if (mTabScrollView != null) { +            return; +        } + +        ScrollingTabContainerView tabScroller = new ScrollingTabContainerView(mContext); + +        if (mHasEmbeddedTabs) { +            tabScroller.setVisibility(View.VISIBLE); +            mActionView.setEmbeddedTabView(tabScroller); +        } else { +            tabScroller.setVisibility(getNavigationMode() == NAVIGATION_MODE_TABS ? +                    View.VISIBLE : View.GONE); +            mContainerView.setTabContainer(tabScroller); +        } +        mTabScrollView = tabScroller; +    } + +    void completeDeferredDestroyActionMode() { +        if (mDeferredModeDestroyCallback != null) { +            mDeferredModeDestroyCallback.onDestroyActionMode(mDeferredDestroyActionMode); +            mDeferredDestroyActionMode = null; +            mDeferredModeDestroyCallback = null; +        } +    } + +    /** +     * Enables or disables animation between show/hide states. +     * If animation is disabled using this method, animations in progress +     * will be finished. +     * +     * @param enabled true to animate, false to not animate. +     */ +    public void setShowHideAnimationEnabled(boolean enabled) { +        mShowHideAnimationEnabled = enabled; +        if (!enabled && mCurrentShowAnim != null) { +            mCurrentShowAnim.end(); +        } +    } + +    public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) { +        mMenuVisibilityListeners.add(listener); +    } + +    public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) { +        mMenuVisibilityListeners.remove(listener); +    } + +    public void dispatchMenuVisibilityChanged(boolean isVisible) { +        if (isVisible == mLastMenuVisibility) { +            return; +        } +        mLastMenuVisibility = isVisible; + +        final int count = mMenuVisibilityListeners.size(); +        for (int i = 0; i < count; i++) { +            mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible); +        } +    } + +    @Override +    public void setCustomView(int resId) { +        setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId, mActionView, false)); +    } + +    @Override +    public void setDisplayUseLogoEnabled(boolean useLogo) { +        setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO); +    } + +    @Override +    public void setDisplayShowHomeEnabled(boolean showHome) { +        setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME); +    } + +    @Override +    public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) { +        setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP); +    } + +    @Override +    public void setDisplayShowTitleEnabled(boolean showTitle) { +        setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE); +    } + +    @Override +    public void setDisplayShowCustomEnabled(boolean showCustom) { +        setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM); +    } + +    @Override +    public void setHomeButtonEnabled(boolean enable) { +        mActionView.setHomeButtonEnabled(enable); +    } + +    @Override +    public void setTitle(int resId) { +        setTitle(mContext.getString(resId)); +    } + +    @Override +    public void setSubtitle(int resId) { +        setSubtitle(mContext.getString(resId)); +    } + +    public void setSelectedNavigationItem(int position) { +        switch (mActionView.getNavigationMode()) { +        case NAVIGATION_MODE_TABS: +            selectTab(mTabs.get(position)); +            break; +        case NAVIGATION_MODE_LIST: +            mActionView.setDropdownSelectedPosition(position); +            break; +        default: +            throw new IllegalStateException( +                    "setSelectedNavigationIndex not valid for current navigation mode"); +        } +    } + +    public void removeAllTabs() { +        cleanupTabs(); +    } + +    private void cleanupTabs() { +        if (mSelectedTab != null) { +            selectTab(null); +        } +        mTabs.clear(); +        if (mTabScrollView != null) { +            mTabScrollView.removeAllTabs(); +        } +        mSavedTabPosition = INVALID_POSITION; +    } + +    public void setTitle(CharSequence title) { +        mActionView.setTitle(title); +    } + +    public void setSubtitle(CharSequence subtitle) { +        mActionView.setSubtitle(subtitle); +    } + +    public void setDisplayOptions(int options) { +        mActionView.setDisplayOptions(options); +    } + +    public void setDisplayOptions(int options, int mask) { +        final int current = mActionView.getDisplayOptions(); +        mActionView.setDisplayOptions((options & mask) | (current & ~mask)); +    } + +    public void setBackgroundDrawable(Drawable d) { +        mContainerView.setPrimaryBackground(d); +    } + +    public void setStackedBackgroundDrawable(Drawable d) { +        mContainerView.setStackedBackground(d); +    } + +    public void setSplitBackgroundDrawable(Drawable d) { +        if (mSplitView != null) { +            mSplitView.setSplitBackground(d); +        } +    } + +    public View getCustomView() { +        return mActionView.getCustomNavigationView(); +    } + +    public CharSequence getTitle() { +        return mActionView.getTitle(); +    } + +    public CharSequence getSubtitle() { +        return mActionView.getSubtitle(); +    } + +    public int getNavigationMode() { +        return mActionView.getNavigationMode(); +    } + +    public int getDisplayOptions() { +        return mActionView.getDisplayOptions(); +    } + +    public ActionMode startActionMode(ActionMode.Callback callback) { +        boolean wasHidden = false; +        if (mActionMode != null) { +            wasHidden = mWasHiddenBeforeMode; +            mActionMode.finish(); +        } + +        mContextView.killMode(); +        ActionModeImpl mode = new ActionModeImpl(callback); +        if (mode.dispatchOnCreate()) { +            mWasHiddenBeforeMode = !isShowing() || wasHidden; +            mode.invalidate(); +            mContextView.initForMode(mode); +            animateToMode(true); +            if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) { +                // TODO animate this +                mSplitView.setVisibility(View.VISIBLE); +            } +            mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); +            mActionMode = mode; +            return mode; +        } +        return null; +    } + +    private void configureTab(Tab tab, int position) { +        final TabImpl tabi = (TabImpl) tab; +        final ActionBar.TabListener callback = tabi.getCallback(); + +        if (callback == null) { +            throw new IllegalStateException("Action Bar Tab must have a Callback"); +        } + +        tabi.setPosition(position); +        mTabs.add(position, tabi); + +        final int count = mTabs.size(); +        for (int i = position + 1; i < count; i++) { +            mTabs.get(i).setPosition(i); +        } +    } + +    @Override +    public void addTab(Tab tab) { +        addTab(tab, mTabs.isEmpty()); +    } + +    @Override +    public void addTab(Tab tab, int position) { +        addTab(tab, position, mTabs.isEmpty()); +    } + +    @Override +    public void addTab(Tab tab, boolean setSelected) { +        ensureTabsExist(); +        mTabScrollView.addTab(tab, setSelected); +        configureTab(tab, mTabs.size()); +        if (setSelected) { +            selectTab(tab); +        } +    } + +    @Override +    public void addTab(Tab tab, int position, boolean setSelected) { +        ensureTabsExist(); +        mTabScrollView.addTab(tab, position, setSelected); +        configureTab(tab, position); +        if (setSelected) { +            selectTab(tab); +        } +    } + +    @Override +    public Tab newTab() { +        return new TabImpl(); +    } + +    @Override +    public void removeTab(Tab tab) { +        removeTabAt(tab.getPosition()); +    } + +    @Override +    public void removeTabAt(int position) { +        if (mTabScrollView == null) { +            // No tabs around to remove +            return; +        } + +        int selectedTabPosition = mSelectedTab != null +                ? mSelectedTab.getPosition() : mSavedTabPosition; +        mTabScrollView.removeTabAt(position); +        TabImpl removedTab = mTabs.remove(position); +        if (removedTab != null) { +            removedTab.setPosition(-1); +        } + +        final int newTabCount = mTabs.size(); +        for (int i = position; i < newTabCount; i++) { +            mTabs.get(i).setPosition(i); +        } + +        if (selectedTabPosition == position) { +            selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1))); +        } +    } + +    @Override +    public void selectTab(Tab tab) { +        if (getNavigationMode() != NAVIGATION_MODE_TABS) { +            mSavedTabPosition = tab != null ? tab.getPosition() : INVALID_POSITION; +            return; +        } + +        FragmentTransaction trans = null; +        if (mActivity instanceof FragmentActivity) { +            trans = ((FragmentActivity)mActivity).getSupportFragmentManager().beginTransaction() +                    .disallowAddToBackStack(); +        } + +        if (mSelectedTab == tab) { +            if (mSelectedTab != null) { +                mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans); +                mTabScrollView.animateToTab(tab.getPosition()); +            } +        } else { +            mTabScrollView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION); +            if (mSelectedTab != null) { +                mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans); +            } +            mSelectedTab = (TabImpl) tab; +            if (mSelectedTab != null) { +                mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans); +            } +        } + +        if (trans != null && !trans.isEmpty()) { +            trans.commit(); +        } +    } + +    @Override +    public Tab getSelectedTab() { +        return mSelectedTab; +    } + +    @Override +    public int getHeight() { +        return mContainerView.getHeight(); +    } + +    @Override +    public void show() { +        show(true); +    } + +    void show(boolean markHiddenBeforeMode) { +        if (mCurrentShowAnim != null) { +            mCurrentShowAnim.end(); +        } +        if (mContainerView.getVisibility() == View.VISIBLE) { +            if (markHiddenBeforeMode) mWasHiddenBeforeMode = false; +            return; +        } +        mContainerView.setVisibility(View.VISIBLE); + +        if (mShowHideAnimationEnabled) { +            mContainerView.setAlpha(0); +            AnimatorSet anim = new AnimatorSet(); +            AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 1)); +            if (mContentView != null) { +                b.with(ObjectAnimator.ofFloat(mContentView, "translationY", +                        -mContainerView.getHeight(), 0)); +                mContainerView.setTranslationY(-mContainerView.getHeight()); +                b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0)); +            } +            if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) { +                mSplitView.setAlpha(0); +                mSplitView.setVisibility(View.VISIBLE); +                b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 1)); +            } +            anim.addListener(mShowListener); +            mCurrentShowAnim = anim; +            anim.start(); +        } else { +            mContainerView.setAlpha(1); +            mContainerView.setTranslationY(0); +            mShowListener.onAnimationEnd(null); +        } +    } + +    @Override +    public void hide() { +        if (mCurrentShowAnim != null) { +            mCurrentShowAnim.end(); +        } +        if (mContainerView.getVisibility() == View.GONE) { +            return; +        } + +        if (mShowHideAnimationEnabled) { +            mContainerView.setAlpha(1); +            mContainerView.setTransitioning(true); +            AnimatorSet anim = new AnimatorSet(); +            AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 0)); +            if (mContentView != null) { +                b.with(ObjectAnimator.ofFloat(mContentView, "translationY", +                        0, -mContainerView.getHeight())); +                b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", +                        -mContainerView.getHeight())); +            } +            if (mSplitView != null && mSplitView.getVisibility() == View.VISIBLE) { +                mSplitView.setAlpha(1); +                b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 0)); +            } +            anim.addListener(mHideListener); +            mCurrentShowAnim = anim; +            anim.start(); +        } else { +            mHideListener.onAnimationEnd(null); +        } +    } + +    public boolean isShowing() { +        return mContainerView.getVisibility() == View.VISIBLE; +    } + +    void animateToMode(boolean toActionMode) { +        if (toActionMode) { +            show(false); +        } +        if (mCurrentModeAnim != null) { +            mCurrentModeAnim.end(); +        } + +        mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); +        mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE); +        if (mTabScrollView != null && !mActionView.hasEmbeddedTabs() && mActionView.isCollapsed()) { +            mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); +        } +    } + +    public Context getThemedContext() { +        if (mThemedContext == null) { +            TypedValue outValue = new TypedValue(); +            Resources.Theme currentTheme = mContext.getTheme(); +            currentTheme.resolveAttribute(R.attr.actionBarWidgetTheme, +                    outValue, true); +            final int targetThemeRes = outValue.resourceId; + +            if (targetThemeRes != 0) { //XXX && mContext.getThemeResId() != targetThemeRes) { +                mThemedContext = new ContextThemeWrapper(mContext, targetThemeRes); +            } else { +                mThemedContext = mContext; +            } +        } +        return mThemedContext; +    } + +    /** +     * @hide +     */ +    public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback { +        private ActionMode.Callback mCallback; +        private MenuBuilder mMenu; +        private WeakReference<View> mCustomView; + +        public ActionModeImpl(ActionMode.Callback callback) { +            mCallback = callback; +            mMenu = new MenuBuilder(getThemedContext()) +                    .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); +            mMenu.setCallback(this); +        } + +        @Override +        public MenuInflater getMenuInflater() { +            return new MenuInflater(getThemedContext()); +        } + +        @Override +        public Menu getMenu() { +            return mMenu; +        } + +        @Override +        public void finish() { +            if (mActionMode != this) { +                // Not the active action mode - no-op +                return; +            } + +            // If we were hidden before the mode was shown, defer the onDestroy +            // callback until the animation is finished and associated relayout +            // is about to happen. This lets apps better anticipate visibility +            // and layout behavior. +            if (mWasHiddenBeforeMode) { +                mDeferredDestroyActionMode = this; +                mDeferredModeDestroyCallback = mCallback; +            } else { +                mCallback.onDestroyActionMode(this); +            } +            mCallback = null; +            animateToMode(false); + +            // Clear out the context mode views after the animation finishes +            mContextView.closeMode(); +            mActionView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + +            mActionMode = null; + +            if (mWasHiddenBeforeMode) { +                hide(); +            } +        } + +        @Override +        public void invalidate() { +            mMenu.stopDispatchingItemsChanged(); +            try { +                mCallback.onPrepareActionMode(this, mMenu); +            } finally { +                mMenu.startDispatchingItemsChanged(); +            } +        } + +        public boolean dispatchOnCreate() { +            mMenu.stopDispatchingItemsChanged(); +            try { +                return mCallback.onCreateActionMode(this, mMenu); +            } finally { +                mMenu.startDispatchingItemsChanged(); +            } +        } + +        @Override +        public void setCustomView(View view) { +            mContextView.setCustomView(view); +            mCustomView = new WeakReference<View>(view); +        } + +        @Override +        public void setSubtitle(CharSequence subtitle) { +            mContextView.setSubtitle(subtitle); +        } + +        @Override +        public void setTitle(CharSequence title) { +            mContextView.setTitle(title); +        } + +        @Override +        public void setTitle(int resId) { +            setTitle(mContext.getResources().getString(resId)); +        } + +        @Override +        public void setSubtitle(int resId) { +            setSubtitle(mContext.getResources().getString(resId)); +        } + +        @Override +        public CharSequence getTitle() { +            return mContextView.getTitle(); +        } + +        @Override +        public CharSequence getSubtitle() { +            return mContextView.getSubtitle(); +        } + +        @Override +        public View getCustomView() { +            return mCustomView != null ? mCustomView.get() : null; +        } + +        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { +            if (mCallback != null) { +                return mCallback.onActionItemClicked(this, item); +            } else { +                return false; +            } +        } + +        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { +        } + +        public boolean onSubMenuSelected(SubMenuBuilder subMenu) { +            if (mCallback == null) { +                return false; +            } + +            if (!subMenu.hasVisibleItems()) { +                return true; +            } + +            new MenuPopupHelper(getThemedContext(), subMenu).show(); +            return true; +        } + +        public void onCloseSubMenu(SubMenuBuilder menu) { +        } + +        public void onMenuModeChange(MenuBuilder menu) { +            if (mCallback == null) { +                return; +            } +            invalidate(); +            mContextView.showOverflowMenu(); +        } +    } + +    /** +     * @hide +     */ +    public class TabImpl extends ActionBar.Tab { +        private ActionBar.TabListener mCallback; +        private Object mTag; +        private Drawable mIcon; +        private CharSequence mText; +        private CharSequence mContentDesc; +        private int mPosition = -1; +        private View mCustomView; + +        @Override +        public Object getTag() { +            return mTag; +        } + +        @Override +        public Tab setTag(Object tag) { +            mTag = tag; +            return this; +        } + +        public ActionBar.TabListener getCallback() { +            return mCallback; +        } + +        @Override +        public Tab setTabListener(ActionBar.TabListener callback) { +            mCallback = callback; +            return this; +        } + +        @Override +        public View getCustomView() { +            return mCustomView; +        } + +        @Override +        public Tab setCustomView(View view) { +            mCustomView = view; +            if (mPosition >= 0) { +                mTabScrollView.updateTab(mPosition); +            } +            return this; +        } + +        @Override +        public Tab setCustomView(int layoutResId) { +            return setCustomView(LayoutInflater.from(getThemedContext()) +                    .inflate(layoutResId, null)); +        } + +        @Override +        public Drawable getIcon() { +            return mIcon; +        } + +        @Override +        public int getPosition() { +            return mPosition; +        } + +        public void setPosition(int position) { +            mPosition = position; +        } + +        @Override +        public CharSequence getText() { +            return mText; +        } + +        @Override +        public Tab setIcon(Drawable icon) { +            mIcon = icon; +            if (mPosition >= 0) { +                mTabScrollView.updateTab(mPosition); +            } +            return this; +        } + +        @Override +        public Tab setIcon(int resId) { +            return setIcon(mContext.getResources().getDrawable(resId)); +        } + +        @Override +        public Tab setText(CharSequence text) { +            mText = text; +            if (mPosition >= 0) { +                mTabScrollView.updateTab(mPosition); +            } +            return this; +        } + +        @Override +        public Tab setText(int resId) { +            return setText(mContext.getResources().getText(resId)); +        } + +        @Override +        public void select() { +            selectTab(this); +        } + +        @Override +        public Tab setContentDescription(int resId) { +            return setContentDescription(mContext.getResources().getText(resId)); +        } + +        @Override +        public Tab setContentDescription(CharSequence contentDesc) { +            mContentDesc = contentDesc; +            if (mPosition >= 0) { +                mTabScrollView.updateTab(mPosition); +            } +            return this; +        } + +        @Override +        public CharSequence getContentDescription() { +            return mContentDesc; +        } +    } + +    @Override +    public void setCustomView(View view) { +        mActionView.setCustomNavigationView(view); +    } + +    @Override +    public void setCustomView(View view, LayoutParams layoutParams) { +        view.setLayoutParams(layoutParams); +        mActionView.setCustomNavigationView(view); +    } + +    @Override +    public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) { +        mActionView.setDropdownAdapter(adapter); +        mActionView.setCallback(callback); +    } + +    @Override +    public int getSelectedNavigationIndex() { +        switch (mActionView.getNavigationMode()) { +            case NAVIGATION_MODE_TABS: +                return mSelectedTab != null ? mSelectedTab.getPosition() : -1; +            case NAVIGATION_MODE_LIST: +                return mActionView.getDropdownSelectedPosition(); +            default: +                return -1; +        } +    } + +    @Override +    public int getNavigationItemCount() { +        switch (mActionView.getNavigationMode()) { +            case NAVIGATION_MODE_TABS: +                return mTabs.size(); +            case NAVIGATION_MODE_LIST: +                SpinnerAdapter adapter = mActionView.getDropdownAdapter(); +                return adapter != null ? adapter.getCount() : 0; +            default: +                return 0; +        } +    } + +    @Override +    public int getTabCount() { +        return mTabs.size(); +    } + +    @Override +    public void setNavigationMode(int mode) { +        final int oldMode = mActionView.getNavigationMode(); +        switch (oldMode) { +            case NAVIGATION_MODE_TABS: +                mSavedTabPosition = getSelectedNavigationIndex(); +                selectTab(null); +                mTabScrollView.setVisibility(View.GONE); +                break; +        } +        mActionView.setNavigationMode(mode); +        switch (mode) { +            case NAVIGATION_MODE_TABS: +                ensureTabsExist(); +                mTabScrollView.setVisibility(View.VISIBLE); +                if (mSavedTabPosition != INVALID_POSITION) { +                    setSelectedNavigationItem(mSavedTabPosition); +                    mSavedTabPosition = INVALID_POSITION; +                } +                break; +        } +        mActionView.setCollapsable(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs); +    } + +    @Override +    public Tab getTabAt(int index) { +        return mTabs.get(index); +    } + + +    @Override +    public void setIcon(int resId) { +        mActionView.setIcon(resId); +    } + +    @Override +    public void setIcon(Drawable icon) { +        mActionView.setIcon(icon); +    } + +    @Override +    public void setLogo(int resId) { +        mActionView.setLogo(resId); +    } + +    @Override +    public void setLogo(Drawable logo) { +        mActionView.setLogo(logo); +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java new file mode 100644 index 000000000..840cb3d27 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java @@ -0,0 +1,468 @@ +package com.actionbarsherlock.internal.app; + +import java.util.HashSet; +import java.util.Set; + +import android.app.Activity; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.view.View; +import android.widget.SpinnerAdapter; + +import com.actionbarsherlock.app.ActionBar; + +public class ActionBarWrapper extends ActionBar implements android.app.ActionBar.OnNavigationListener, android.app.ActionBar.OnMenuVisibilityListener { +    private final Activity mActivity; +    private final android.app.ActionBar mActionBar; +    private ActionBar.OnNavigationListener mNavigationListener; +    private Set<OnMenuVisibilityListener> mMenuVisibilityListeners = new HashSet<OnMenuVisibilityListener>(1); +    private FragmentTransaction mFragmentTransaction; + + +    public ActionBarWrapper(Activity activity) { +        mActivity = activity; +        mActionBar = activity.getActionBar(); +        if (mActionBar != null) { +            mActionBar.addOnMenuVisibilityListener(this); +        } +    } + + +    @Override +    public void setHomeButtonEnabled(boolean enabled) { +        mActionBar.setHomeButtonEnabled(enabled); +    } + +    @Override +    public Context getThemedContext() { +        return mActionBar.getThemedContext(); +    } + +    @Override +    public void setCustomView(View view) { +        mActionBar.setCustomView(view); +    } + +    @Override +    public void setCustomView(View view, LayoutParams layoutParams) { +        android.app.ActionBar.LayoutParams lp = new android.app.ActionBar.LayoutParams(layoutParams); +        lp.gravity = layoutParams.gravity; +        lp.bottomMargin = layoutParams.bottomMargin; +        lp.topMargin = layoutParams.topMargin; +        lp.leftMargin = layoutParams.leftMargin; +        lp.rightMargin = layoutParams.rightMargin; +        mActionBar.setCustomView(view, lp); +    } + +    @Override +    public void setCustomView(int resId) { +        mActionBar.setCustomView(resId); +    } + +    @Override +    public void setIcon(int resId) { +        mActionBar.setIcon(resId); +    } + +    @Override +    public void setIcon(Drawable icon) { +        mActionBar.setIcon(icon); +    } + +    @Override +    public void setLogo(int resId) { +        mActionBar.setLogo(resId); +    } + +    @Override +    public void setLogo(Drawable logo) { +        mActionBar.setLogo(logo); +    } + +    @Override +    public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) { +        mNavigationListener = callback; +        mActionBar.setListNavigationCallbacks(adapter, (callback != null) ? this : null); +    } + +    @Override +    public boolean onNavigationItemSelected(int itemPosition, long itemId) { +        //This should never be a NullPointerException since we only set +        //ourselves as the listener when the callback is not null. +        return mNavigationListener.onNavigationItemSelected(itemPosition, itemId); +    } + +    @Override +    public void setSelectedNavigationItem(int position) { +        mActionBar.setSelectedNavigationItem(position); +    } + +    @Override +    public int getSelectedNavigationIndex() { +        return mActionBar.getSelectedNavigationIndex(); +    } + +    @Override +    public int getNavigationItemCount() { +        return mActionBar.getNavigationItemCount(); +    } + +    @Override +    public void setTitle(CharSequence title) { +        mActionBar.setTitle(title); +    } + +    @Override +    public void setTitle(int resId) { +        mActionBar.setTitle(resId); +    } + +    @Override +    public void setSubtitle(CharSequence subtitle) { +        mActionBar.setSubtitle(subtitle); +    } + +    @Override +    public void setSubtitle(int resId) { +        mActionBar.setSubtitle(resId); +    } + +    @Override +    public void setDisplayOptions(int options) { +        mActionBar.setDisplayOptions(options); +    } + +    @Override +    public void setDisplayOptions(int options, int mask) { +        mActionBar.setDisplayOptions(options, mask); +    } + +    @Override +    public void setDisplayUseLogoEnabled(boolean useLogo) { +        mActionBar.setDisplayUseLogoEnabled(useLogo); +    } + +    @Override +    public void setDisplayShowHomeEnabled(boolean showHome) { +        mActionBar.setDisplayShowHomeEnabled(showHome); +    } + +    @Override +    public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) { +        mActionBar.setDisplayHomeAsUpEnabled(showHomeAsUp); +    } + +    @Override +    public void setDisplayShowTitleEnabled(boolean showTitle) { +        mActionBar.setDisplayShowTitleEnabled(showTitle); +    } + +    @Override +    public void setDisplayShowCustomEnabled(boolean showCustom) { +        mActionBar.setDisplayShowCustomEnabled(showCustom); +    } + +    @Override +    public void setBackgroundDrawable(Drawable d) { +        mActionBar.setBackgroundDrawable(d); +    } + +    @Override +    public void setStackedBackgroundDrawable(Drawable d) { +        mActionBar.setStackedBackgroundDrawable(d); +    } + +    @Override +    public void setSplitBackgroundDrawable(Drawable d) { +        mActionBar.setSplitBackgroundDrawable(d); +    } + +    @Override +    public View getCustomView() { +        return mActionBar.getCustomView(); +    } + +    @Override +    public CharSequence getTitle() { +        return mActionBar.getTitle(); +    } + +    @Override +    public CharSequence getSubtitle() { +        return mActionBar.getSubtitle(); +    } + +    @Override +    public int getNavigationMode() { +        return mActionBar.getNavigationMode(); +    } + +    @Override +    public void setNavigationMode(int mode) { +        mActionBar.setNavigationMode(mode); +    } + +    @Override +    public int getDisplayOptions() { +        return mActionBar.getDisplayOptions(); +    } + +    public class TabWrapper extends ActionBar.Tab implements android.app.ActionBar.TabListener { +        final android.app.ActionBar.Tab mNativeTab; +        private Object mTag; +        private TabListener mListener; + +        public TabWrapper(android.app.ActionBar.Tab nativeTab) { +            mNativeTab = nativeTab; +            mNativeTab.setTag(this); +        } + +        @Override +        public int getPosition() { +            return mNativeTab.getPosition(); +        } + +        @Override +        public Drawable getIcon() { +            return mNativeTab.getIcon(); +        } + +        @Override +        public CharSequence getText() { +            return mNativeTab.getText(); +        } + +        @Override +        public Tab setIcon(Drawable icon) { +            mNativeTab.setIcon(icon); +            return this; +        } + +        @Override +        public Tab setIcon(int resId) { +            mNativeTab.setIcon(resId); +            return this; +        } + +        @Override +        public Tab setText(CharSequence text) { +            mNativeTab.setText(text); +            return this; +        } + +        @Override +        public Tab setText(int resId) { +            mNativeTab.setText(resId); +            return this; +        } + +        @Override +        public Tab setCustomView(View view) { +            mNativeTab.setCustomView(view); +            return this; +        } + +        @Override +        public Tab setCustomView(int layoutResId) { +            mNativeTab.setCustomView(layoutResId); +            return this; +        } + +        @Override +        public View getCustomView() { +            return mNativeTab.getCustomView(); +        } + +        @Override +        public Tab setTag(Object obj) { +            mTag = obj; +            return this; +        } + +        @Override +        public Object getTag() { +            return mTag; +        } + +        @Override +        public Tab setTabListener(TabListener listener) { +            mNativeTab.setTabListener(listener != null ? this : null); +            mListener = listener; +            return this; +        } + +        @Override +        public void select() { +            mNativeTab.select(); +        } + +        @Override +        public Tab setContentDescription(int resId) { +            mNativeTab.setContentDescription(resId); +            return this; +        } + +        @Override +        public Tab setContentDescription(CharSequence contentDesc) { +            mNativeTab.setContentDescription(contentDesc); +            return this; +        } + +        @Override +        public CharSequence getContentDescription() { +            return mNativeTab.getContentDescription(); +        } + +        @Override +        public void onTabReselected(android.app.ActionBar.Tab tab, android.app.FragmentTransaction ft) { +            if (mListener != null) { +                FragmentTransaction trans = null; +                if (mActivity instanceof FragmentActivity) { +                    trans = ((FragmentActivity)mActivity).getSupportFragmentManager().beginTransaction() +                            .disallowAddToBackStack(); +                } + +                mListener.onTabReselected(this, trans); + +                if (trans != null && !trans.isEmpty()) { +                    trans.commit(); +                } +            } +        } + +        @Override +        public void onTabSelected(android.app.ActionBar.Tab tab, android.app.FragmentTransaction ft) { +            if (mListener != null) { + +                if (mFragmentTransaction == null && mActivity instanceof FragmentActivity) { +                    mFragmentTransaction = ((FragmentActivity)mActivity).getSupportFragmentManager().beginTransaction() +                            .disallowAddToBackStack(); +                } + +                mListener.onTabSelected(this, mFragmentTransaction); + +                if (mFragmentTransaction != null) { +                    if (!mFragmentTransaction.isEmpty()) { +                        mFragmentTransaction.commit(); +                    } +                    mFragmentTransaction = null; +                } +            } +        } + +        @Override +        public void onTabUnselected(android.app.ActionBar.Tab tab, android.app.FragmentTransaction ft) { +            if (mListener != null) { +                FragmentTransaction trans = null; +                if (mActivity instanceof FragmentActivity) { +                    trans = ((FragmentActivity)mActivity).getSupportFragmentManager().beginTransaction() +                            .disallowAddToBackStack(); +                    mFragmentTransaction = trans; +                } + +                mListener.onTabUnselected(this, trans); +            } +        } +    } + +    @Override +    public Tab newTab() { +        return new TabWrapper(mActionBar.newTab()); +    } + +    @Override +    public void addTab(Tab tab) { +        mActionBar.addTab(((TabWrapper)tab).mNativeTab); +    } + +    @Override +    public void addTab(Tab tab, boolean setSelected) { +        mActionBar.addTab(((TabWrapper)tab).mNativeTab, setSelected); +    } + +    @Override +    public void addTab(Tab tab, int position) { +        mActionBar.addTab(((TabWrapper)tab).mNativeTab, position); +    } + +    @Override +    public void addTab(Tab tab, int position, boolean setSelected) { +        mActionBar.addTab(((TabWrapper)tab).mNativeTab, position, setSelected); +    } + +    @Override +    public void removeTab(Tab tab) { +        mActionBar.removeTab(((TabWrapper)tab).mNativeTab); +    } + +    @Override +    public void removeTabAt(int position) { +        mActionBar.removeTabAt(position); +    } + +    @Override +    public void removeAllTabs() { +        mActionBar.removeAllTabs(); +    } + +    @Override +    public void selectTab(Tab tab) { +        mActionBar.selectTab(((TabWrapper)tab).mNativeTab); +    } + +    @Override +    public Tab getSelectedTab() { +        android.app.ActionBar.Tab selected = mActionBar.getSelectedTab(); +        return (selected != null) ? (Tab)selected.getTag() : null; +    } + +    @Override +    public Tab getTabAt(int index) { +        android.app.ActionBar.Tab selected = mActionBar.getTabAt(index); +        return (selected != null) ? (Tab)selected.getTag() : null; +    } + +    @Override +    public int getTabCount() { +        return mActionBar.getTabCount(); +    } + +    @Override +    public int getHeight() { +        return mActionBar.getHeight(); +    } + +    @Override +    public void show() { +        mActionBar.show(); +    } + +    @Override +    public void hide() { +        mActionBar.hide(); +    } + +    @Override +    public boolean isShowing() { +        return mActionBar.isShowing(); +    } + +    @Override +    public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) { +        mMenuVisibilityListeners.add(listener); +    } + +    @Override +    public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) { +        mMenuVisibilityListeners.remove(listener); +    } + +    @Override +    public void onMenuVisibilityChanged(boolean isVisible) { +        for (OnMenuVisibilityListener listener : mMenuVisibilityListeners) { +            listener.onMenuVisibilityChanged(isVisible); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Animator.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Animator.java new file mode 100644 index 000000000..2caf5b4a9 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Animator.java @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +import java.util.ArrayList; + +import android.view.animation.Interpolator; + +/** + * This is the superclass for classes which provide basic support for animations which can be + * started, ended, and have <code>AnimatorListeners</code> added to them. + */ +public abstract class Animator implements Cloneable { + + +    /** +     * The set of listeners to be sent events through the life of an animation. +     */ +    ArrayList<AnimatorListener> mListeners = null; + +    /** +     * Starts this animation. If the animation has a nonzero startDelay, the animation will start +     * running after that delay elapses. A non-delayed animation will have its initial +     * value(s) set immediately, followed by calls to +     * {@link AnimatorListener#onAnimationStart(Animator)} for any listeners of this animator. +     * +     * <p>The animation started by calling this method will be run on the thread that called +     * this method. This thread should have a Looper on it (a runtime exception will be thrown if +     * this is not the case). Also, if the animation will animate +     * properties of objects in the view hierarchy, then the calling thread should be the UI +     * thread for that view hierarchy.</p> +     * +     */ +    public void start() { +    } + +    /** +     * Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to +     * stop in its tracks, sending an +     * {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to +     * its listeners, followed by an +     * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message. +     * +     * <p>This method must be called on the thread that is running the animation.</p> +     */ +    public void cancel() { +    } + +    /** +     * Ends the animation. This causes the animation to assign the end value of the property being +     * animated, then calling the +     * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on +     * its listeners. +     * +     * <p>This method must be called on the thread that is running the animation.</p> +     */ +    public void end() { +    } + +    /** +     * The amount of time, in milliseconds, to delay starting the animation after +     * {@link #start()} is called. +     * +     * @return the number of milliseconds to delay running the animation +     */ +    public abstract long getStartDelay(); + +    /** +     * The amount of time, in milliseconds, to delay starting the animation after +     * {@link #start()} is called. + +     * @param startDelay The amount of the delay, in milliseconds +     */ +    public abstract void setStartDelay(long startDelay); + + +    /** +     * Sets the length of the animation. +     * +     * @param duration The length of the animation, in milliseconds. +     */ +    public abstract Animator setDuration(long duration); + +    /** +     * Gets the length of the animation. +     * +     * @return The length of the animation, in milliseconds. +     */ +    public abstract long getDuration(); + +    /** +     * The time interpolator used in calculating the elapsed fraction of this animation. The +     * interpolator determines whether the animation runs with linear or non-linear motion, +     * such as acceleration and deceleration. The default value is +     * {@link android.view.animation.AccelerateDecelerateInterpolator} +     * +     * @param value the interpolator to be used by this animation +     */ +    public abstract void setInterpolator(/*Time*/Interpolator value); + +    /** +     * Returns whether this Animator is currently running (having been started and gone past any +     * initial startDelay period and not yet ended). +     * +     * @return Whether the Animator is running. +     */ +    public abstract boolean isRunning(); + +    /** +     * Returns whether this Animator has been started and not yet ended. This state is a superset +     * of the state of {@link #isRunning()}, because an Animator with a nonzero +     * {@link #getStartDelay() startDelay} will return true for {@link #isStarted()} during the +     * delay phase, whereas {@link #isRunning()} will return true only after the delay phase +     * is complete. +     * +     * @return Whether the Animator has been started and not yet ended. +     */ +    public boolean isStarted() { +        // Default method returns value for isRunning(). Subclasses should override to return a +        // real value. +        return isRunning(); +    } + +    /** +     * Adds a listener to the set of listeners that are sent events through the life of an +     * animation, such as start, repeat, and end. +     * +     * @param listener the listener to be added to the current set of listeners for this animation. +     */ +    public void addListener(AnimatorListener listener) { +        if (mListeners == null) { +            mListeners = new ArrayList<AnimatorListener>(); +        } +        mListeners.add(listener); +    } + +    /** +     * Removes a listener from the set listening to this animation. +     * +     * @param listener the listener to be removed from the current set of listeners for this +     *                 animation. +     */ +    public void removeListener(AnimatorListener listener) { +        if (mListeners == null) { +            return; +        } +        mListeners.remove(listener); +        if (mListeners.size() == 0) { +            mListeners = null; +        } +    } + +    /** +     * Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently +     * listening for events on this <code>Animator</code> object. +     * +     * @return ArrayList<AnimatorListener> The set of listeners. +     */ +    public ArrayList<AnimatorListener> getListeners() { +        return mListeners; +    } + +    /** +     * Removes all listeners from this object. This is equivalent to calling +     * <code>getListeners()</code> followed by calling <code>clear()</code> on the +     * returned list of listeners. +     */ +    public void removeAllListeners() { +        if (mListeners != null) { +            mListeners.clear(); +            mListeners = null; +        } +    } + +    @Override +    public Animator clone() { +        try { +            final Animator anim = (Animator) super.clone(); +            if (mListeners != null) { +                ArrayList<AnimatorListener> oldListeners = mListeners; +                anim.mListeners = new ArrayList<AnimatorListener>(); +                int numListeners = oldListeners.size(); +                for (int i = 0; i < numListeners; ++i) { +                    anim.mListeners.add(oldListeners.get(i)); +                } +            } +            return anim; +        } catch (CloneNotSupportedException e) { +           throw new AssertionError(); +        } +    } + +    /** +     * This method tells the object to use appropriate information to extract +     * starting values for the animation. For example, a AnimatorSet object will pass +     * this call to its child objects to tell them to set up the values. A +     * ObjectAnimator object will use the information it has about its target object +     * and PropertyValuesHolder objects to get the start values for its properties. +     * An ValueAnimator object will ignore the request since it does not have enough +     * information (such as a target object) to gather these values. +     */ +    public void setupStartValues() { +    } + +    /** +     * This method tells the object to use appropriate information to extract +     * ending values for the animation. For example, a AnimatorSet object will pass +     * this call to its child objects to tell them to set up the values. A +     * ObjectAnimator object will use the information it has about its target object +     * and PropertyValuesHolder objects to get the start values for its properties. +     * An ValueAnimator object will ignore the request since it does not have enough +     * information (such as a target object) to gather these values. +     */ +    public void setupEndValues() { +    } + +    /** +     * Sets the target object whose property will be animated by this animation. Not all subclasses +     * operate on target objects (for example, {@link ValueAnimator}, but this method +     * is on the superclass for the convenience of dealing generically with those subclasses +     * that do handle targets. +     * +     * @param target The object being animated +     */ +    public void setTarget(Object target) { +    } + +    /** +     * <p>An animation listener receives notifications from an animation. +     * Notifications indicate animation related events, such as the end or the +     * repetition of the animation.</p> +     */ +    public static interface AnimatorListener { +        /** +         * <p>Notifies the start of the animation.</p> +         * +         * @param animation The started animation. +         */ +        void onAnimationStart(Animator animation); + +        /** +         * <p>Notifies the end of the animation. This callback is not invoked +         * for animations with repeat count set to INFINITE.</p> +         * +         * @param animation The animation which reached its end. +         */ +        void onAnimationEnd(Animator animation); + +        /** +         * <p>Notifies the cancellation of the animation. This callback is not invoked +         * for animations with repeat count set to INFINITE.</p> +         * +         * @param animation The animation which was canceled. +         */ +        void onAnimationCancel(Animator animation); + +        /** +         * <p>Notifies the repetition of the animation.</p> +         * +         * @param animation The animation which was repeated. +         */ +        void onAnimationRepeat(Animator animation); +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorListenerAdapter.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorListenerAdapter.java new file mode 100644 index 000000000..02ddff48d --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorListenerAdapter.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +/** + * This adapter class provides empty implementations of the methods from {@link android.animation.Animator.AnimatorListener}. + * Any custom listener that cares only about a subset of the methods of this listener can + * simply subclass this adapter class instead of implementing the interface directly. + */ +public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener { + +    /** +     * {@inheritDoc} +     */ +    @Override +    public void onAnimationCancel(Animator animation) { +    } + +    /** +     * {@inheritDoc} +     */ +    @Override +    public void onAnimationEnd(Animator animation) { +    } + +    /** +     * {@inheritDoc} +     */ +    @Override +    public void onAnimationRepeat(Animator animation) { +    } + +    /** +     * {@inheritDoc} +     */ +    @Override +    public void onAnimationStart(Animator animation) { +    } + +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorSet.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorSet.java new file mode 100644 index 000000000..3231080c4 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorSet.java @@ -0,0 +1,1111 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; + +import android.view.animation.Interpolator; + +/** + * This class plays a set of {@link Animator} objects in the specified order. Animations + * can be set up to play together, in sequence, or after a specified delay. + * + * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>: + * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or + * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add + * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be + * used in conjunction with methods in the {@link AnimatorSet.Builder Builder} + * class to add animations + * one by one.</p> + * + * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between + * its animations. For example, an animation a1 could be set up to start before animation a2, a2 + * before a3, and a3 before a1. The results of this configuration are undefined, but will typically + * result in none of the affected animations being played. Because of this (and because + * circular dependencies do not make logical sense anyway), circular dependencies + * should be avoided, and the dependency flow of animations should only be in one direction. + */ +@SuppressWarnings("unchecked") +public final class AnimatorSet extends Animator { + +    /** +     * Internal variables +     * NOTE: This object implements the clone() method, making a deep copy of any referenced +     * objects. As other non-trivial fields are added to this class, make sure to add logic +     * to clone() to make deep copies of them. +     */ + +    /** +     * Tracks animations currently being played, so that we know what to +     * cancel or end when cancel() or end() is called on this AnimatorSet +     */ +    private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>(); + +    /** +     * Contains all nodes, mapped to their respective Animators. When new +     * dependency information is added for an Animator, we want to add it +     * to a single node representing that Animator, not create a new Node +     * if one already exists. +     */ +    private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>(); + +    /** +     * Set of all nodes created for this AnimatorSet. This list is used upon +     * starting the set, and the nodes are placed in sorted order into the +     * sortedNodes collection. +     */ +    private ArrayList<Node> mNodes = new ArrayList<Node>(); + +    /** +     * The sorted list of nodes. This is the order in which the animations will +     * be played. The details about when exactly they will be played depend +     * on the dependency relationships of the nodes. +     */ +    private ArrayList<Node> mSortedNodes = new ArrayList<Node>(); + +    /** +     * Flag indicating whether the nodes should be sorted prior to playing. This +     * flag allows us to cache the previous sorted nodes so that if the sequence +     * is replayed with no changes, it does not have to re-sort the nodes again. +     */ +    private boolean mNeedsSort = true; + +    private AnimatorSetListener mSetListener = null; + +    /** +     * Flag indicating that the AnimatorSet has been manually +     * terminated (by calling cancel() or end()). +     * This flag is used to avoid starting other animations when currently-playing +     * child animations of this AnimatorSet end. It also determines whether cancel/end +     * notifications are sent out via the normal AnimatorSetListener mechanism. +     */ +    boolean mTerminated = false; + +    /** +     * Indicates whether an AnimatorSet has been start()'d, whether or +     * not there is a nonzero startDelay. +     */ +    private boolean mStarted = false; + +    // The amount of time in ms to delay starting the animation after start() is called +    private long mStartDelay = 0; + +    // Animator used for a nonzero startDelay +    private ValueAnimator mDelayAnim = null; + + +    // How long the child animations should last in ms. The default value is negative, which +    // simply means that there is no duration set on the AnimatorSet. When a real duration is +    // set, it is passed along to the child animations. +    private long mDuration = -1; + + +    /** +     * Sets up this AnimatorSet to play all of the supplied animations at the same time. +     * +     * @param items The animations that will be started simultaneously. +     */ +    public void playTogether(Animator... items) { +        if (items != null) { +            mNeedsSort = true; +            Builder builder = play(items[0]); +            for (int i = 1; i < items.length; ++i) { +                builder.with(items[i]); +            } +        } +    } + +    /** +     * Sets up this AnimatorSet to play all of the supplied animations at the same time. +     * +     * @param items The animations that will be started simultaneously. +     */ +    public void playTogether(Collection<Animator> items) { +        if (items != null && items.size() > 0) { +            mNeedsSort = true; +            Builder builder = null; +            for (Animator anim : items) { +                if (builder == null) { +                    builder = play(anim); +                } else { +                    builder.with(anim); +                } +            } +        } +    } + +    /** +     * Sets up this AnimatorSet to play each of the supplied animations when the +     * previous animation ends. +     * +     * @param items The animations that will be started one after another. +     */ +    public void playSequentially(Animator... items) { +        if (items != null) { +            mNeedsSort = true; +            if (items.length == 1) { +                play(items[0]); +            } else { +                for (int i = 0; i < items.length - 1; ++i) { +                    play(items[i]).before(items[i+1]); +                } +            } +        } +    } + +    /** +     * Sets up this AnimatorSet to play each of the supplied animations when the +     * previous animation ends. +     * +     * @param items The animations that will be started one after another. +     */ +    public void playSequentially(List<Animator> items) { +        if (items != null && items.size() > 0) { +            mNeedsSort = true; +            if (items.size() == 1) { +                play(items.get(0)); +            } else { +                for (int i = 0; i < items.size() - 1; ++i) { +                    play(items.get(i)).before(items.get(i+1)); +                } +            } +        } +    } + +    /** +     * Returns the current list of child Animator objects controlled by this +     * AnimatorSet. This is a copy of the internal list; modifications to the returned list +     * will not affect the AnimatorSet, although changes to the underlying Animator objects +     * will affect those objects being managed by the AnimatorSet. +     * +     * @return ArrayList<Animator> The list of child animations of this AnimatorSet. +     */ +    public ArrayList<Animator> getChildAnimations() { +        ArrayList<Animator> childList = new ArrayList<Animator>(); +        for (Node node : mNodes) { +            childList.add(node.animation); +        } +        return childList; +    } + +    /** +     * Sets the target object for all current {@link #getChildAnimations() child animations} +     * of this AnimatorSet that take targets ({@link ObjectAnimator} and +     * AnimatorSet). +     * +     * @param target The object being animated +     */ +    @Override +    public void setTarget(Object target) { +        for (Node node : mNodes) { +            Animator animation = node.animation; +            if (animation instanceof AnimatorSet) { +                ((AnimatorSet)animation).setTarget(target); +            } else if (animation instanceof ObjectAnimator) { +                ((ObjectAnimator)animation).setTarget(target); +            } +        } +    } + +    /** +     * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations} +     * of this AnimatorSet. +     * +     * @param interpolator the interpolator to be used by each child animation of this AnimatorSet +     */ +    @Override +    public void setInterpolator(/*Time*/Interpolator interpolator) { +        for (Node node : mNodes) { +            node.animation.setInterpolator(interpolator); +        } +    } + +    /** +     * This method creates a <code>Builder</code> object, which is used to +     * set up playing constraints. This initial <code>play()</code> method +     * tells the <code>Builder</code> the animation that is the dependency for +     * the succeeding commands to the <code>Builder</code>. For example, +     * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play +     * <code>a1</code> and <code>a2</code> at the same time, +     * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play +     * <code>a1</code> first, followed by <code>a2</code>, and +     * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play +     * <code>a2</code> first, followed by <code>a1</code>. +     * +     * <p>Note that <code>play()</code> is the only way to tell the +     * <code>Builder</code> the animation upon which the dependency is created, +     * so successive calls to the various functions in <code>Builder</code> +     * will all refer to the initial parameter supplied in <code>play()</code> +     * as the dependency of the other animations. For example, calling +     * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code> +     * and <code>a3</code> when a1 ends; it does not set up a dependency between +     * <code>a2</code> and <code>a3</code>.</p> +     * +     * @param anim The animation that is the dependency used in later calls to the +     * methods in the returned <code>Builder</code> object. A null parameter will result +     * in a null <code>Builder</code> return value. +     * @return Builder The object that constructs the AnimatorSet based on the dependencies +     * outlined in the calls to <code>play</code> and the other methods in the +     * <code>Builder</code object. +     */ +    public Builder play(Animator anim) { +        if (anim != null) { +            mNeedsSort = true; +            return new Builder(anim); +        } +        return null; +    } + +    /** +     * {@inheritDoc} +     * +     * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it +     * is responsible for.</p> +     */ +    @Override +    public void cancel() { +        mTerminated = true; +        if (isStarted()) { +            ArrayList<AnimatorListener> tmpListeners = null; +            if (mListeners != null) { +                tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); +                for (AnimatorListener listener : tmpListeners) { +                    listener.onAnimationCancel(this); +                } +            } +            if (mDelayAnim != null && mDelayAnim.isRunning()) { +                // If we're currently in the startDelay period, just cancel that animator and +                // send out the end event to all listeners +                mDelayAnim.cancel(); +            } else  if (mSortedNodes.size() > 0) { +                for (Node node : mSortedNodes) { +                    node.animation.cancel(); +                } +            } +            if (tmpListeners != null) { +                for (AnimatorListener listener : tmpListeners) { +                    listener.onAnimationEnd(this); +                } +            } +            mStarted = false; +        } +    } + +    /** +     * {@inheritDoc} +     * +     * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is +     * responsible for.</p> +     */ +    @Override +    public void end() { +        mTerminated = true; +        if (isStarted()) { +            if (mSortedNodes.size() != mNodes.size()) { +                // hasn't been started yet - sort the nodes now, then end them +                sortNodes(); +                for (Node node : mSortedNodes) { +                    if (mSetListener == null) { +                        mSetListener = new AnimatorSetListener(this); +                    } +                    node.animation.addListener(mSetListener); +                } +            } +            if (mDelayAnim != null) { +                mDelayAnim.cancel(); +            } +            if (mSortedNodes.size() > 0) { +                for (Node node : mSortedNodes) { +                    node.animation.end(); +                } +            } +            if (mListeners != null) { +                ArrayList<AnimatorListener> tmpListeners = +                        (ArrayList<AnimatorListener>) mListeners.clone(); +                for (AnimatorListener listener : tmpListeners) { +                    listener.onAnimationEnd(this); +                } +            } +            mStarted = false; +        } +    } + +    /** +     * Returns true if any of the child animations of this AnimatorSet have been started and have +     * not yet ended. +     * @return Whether this AnimatorSet has been started and has not yet ended. +     */ +    @Override +    public boolean isRunning() { +        for (Node node : mNodes) { +            if (node.animation.isRunning()) { +                return true; +            } +        } +        return false; +    } + +    @Override +    public boolean isStarted() { +        return mStarted; +    } + +    /** +     * The amount of time, in milliseconds, to delay starting the animation after +     * {@link #start()} is called. +     * +     * @return the number of milliseconds to delay running the animation +     */ +    @Override +    public long getStartDelay() { +        return mStartDelay; +    } + +    /** +     * The amount of time, in milliseconds, to delay starting the animation after +     * {@link #start()} is called. + +     * @param startDelay The amount of the delay, in milliseconds +     */ +    @Override +    public void setStartDelay(long startDelay) { +        mStartDelay = startDelay; +    } + +    /** +     * Gets the length of each of the child animations of this AnimatorSet. This value may +     * be less than 0, which indicates that no duration has been set on this AnimatorSet +     * and each of the child animations will use their own duration. +     * +     * @return The length of the animation, in milliseconds, of each of the child +     * animations of this AnimatorSet. +     */ +    @Override +    public long getDuration() { +        return mDuration; +    } + +    /** +     * Sets the length of each of the current child animations of this AnimatorSet. By default, +     * each child animation will use its own duration. If the duration is set on the AnimatorSet, +     * then each child animation inherits this duration. +     * +     * @param duration The length of the animation, in milliseconds, of each of the child +     * animations of this AnimatorSet. +     */ +    @Override +    public AnimatorSet setDuration(long duration) { +        if (duration < 0) { +            throw new IllegalArgumentException("duration must be a value of zero or greater"); +        } +        for (Node node : mNodes) { +            // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to +            // insert "play-after" delays +            node.animation.setDuration(duration); +        } +        mDuration = duration; +        return this; +    } + +    @Override +    public void setupStartValues() { +        for (Node node : mNodes) { +            node.animation.setupStartValues(); +        } +    } + +    @Override +    public void setupEndValues() { +        for (Node node : mNodes) { +            node.animation.setupEndValues(); +        } +    } + +    /** +     * {@inheritDoc} +     * +     * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which +     * it is responsible. The details of when exactly those animations are started depends on +     * the dependency relationships that have been set up between the animations. +     */ +    @Override +    public void start() { +        mTerminated = false; +        mStarted = true; + +        // First, sort the nodes (if necessary). This will ensure that sortedNodes +        // contains the animation nodes in the correct order. +        sortNodes(); + +        int numSortedNodes = mSortedNodes.size(); +        for (int i = 0; i < numSortedNodes; ++i) { +            Node node = mSortedNodes.get(i); +            // First, clear out the old listeners +            ArrayList<AnimatorListener> oldListeners = node.animation.getListeners(); +            if (oldListeners != null && oldListeners.size() > 0) { +                final ArrayList<AnimatorListener> clonedListeners = new +                        ArrayList<AnimatorListener>(oldListeners); + +                for (AnimatorListener listener : clonedListeners) { +                    if (listener instanceof DependencyListener || +                            listener instanceof AnimatorSetListener) { +                        node.animation.removeListener(listener); +                    } +                } +            } +        } + +        // nodesToStart holds the list of nodes to be started immediately. We don't want to +        // start the animations in the loop directly because we first need to set up +        // dependencies on all of the nodes. For example, we don't want to start an animation +        // when some other animation also wants to start when the first animation begins. +        final ArrayList<Node> nodesToStart = new ArrayList<Node>(); +        for (int i = 0; i < numSortedNodes; ++i) { +            Node node = mSortedNodes.get(i); +            if (mSetListener == null) { +                mSetListener = new AnimatorSetListener(this); +            } +            if (node.dependencies == null || node.dependencies.size() == 0) { +                nodesToStart.add(node); +            } else { +                int numDependencies = node.dependencies.size(); +                for (int j = 0; j < numDependencies; ++j) { +                    Dependency dependency = node.dependencies.get(j); +                    dependency.node.animation.addListener( +                            new DependencyListener(this, node, dependency.rule)); +                } +                node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone(); +            } +            node.animation.addListener(mSetListener); +        } +        // Now that all dependencies are set up, start the animations that should be started. +        if (mStartDelay <= 0) { +            for (Node node : nodesToStart) { +                node.animation.start(); +                mPlayingSet.add(node.animation); +            } +        } else { +            mDelayAnim = ValueAnimator.ofFloat(0f, 1f); +            mDelayAnim.setDuration(mStartDelay); +            mDelayAnim.addListener(new AnimatorListenerAdapter() { +                boolean canceled = false; +                public void onAnimationCancel(Animator anim) { +                    canceled = true; +                } +                public void onAnimationEnd(Animator anim) { +                    if (!canceled) { +                        int numNodes = nodesToStart.size(); +                        for (int i = 0; i < numNodes; ++i) { +                            Node node = nodesToStart.get(i); +                            node.animation.start(); +                            mPlayingSet.add(node.animation); +                        } +                    } +                } +            }); +            mDelayAnim.start(); +        } +        if (mListeners != null) { +            ArrayList<AnimatorListener> tmpListeners = +                    (ArrayList<AnimatorListener>) mListeners.clone(); +            int numListeners = tmpListeners.size(); +            for (int i = 0; i < numListeners; ++i) { +                tmpListeners.get(i).onAnimationStart(this); +            } +        } +        if (mNodes.size() == 0 && mStartDelay == 0) { +            // Handle unusual case where empty AnimatorSet is started - should send out +            // end event immediately since the event will not be sent out at all otherwise +            mStarted = false; +            if (mListeners != null) { +                ArrayList<AnimatorListener> tmpListeners = +                        (ArrayList<AnimatorListener>) mListeners.clone(); +                int numListeners = tmpListeners.size(); +                for (int i = 0; i < numListeners; ++i) { +                    tmpListeners.get(i).onAnimationEnd(this); +                } +            } +        } +    } + +    @Override +    public AnimatorSet clone() { +        final AnimatorSet anim = (AnimatorSet) super.clone(); +        /* +         * The basic clone() operation copies all items. This doesn't work very well for +         * AnimatorSet, because it will copy references that need to be recreated and state +         * that may not apply. What we need to do now is put the clone in an uninitialized +         * state, with fresh, empty data structures. Then we will build up the nodes list +         * manually, as we clone each Node (and its animation). The clone will then be sorted, +         * and will populate any appropriate lists, when it is started. +         */ +        anim.mNeedsSort = true; +        anim.mTerminated = false; +        anim.mStarted = false; +        anim.mPlayingSet = new ArrayList<Animator>(); +        anim.mNodeMap = new HashMap<Animator, Node>(); +        anim.mNodes = new ArrayList<Node>(); +        anim.mSortedNodes = new ArrayList<Node>(); + +        // Walk through the old nodes list, cloning each node and adding it to the new nodemap. +        // One problem is that the old node dependencies point to nodes in the old AnimatorSet. +        // We need to track the old/new nodes in order to reconstruct the dependencies in the clone. +        HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new> +        for (Node node : mNodes) { +            Node nodeClone = node.clone(); +            nodeCloneMap.put(node, nodeClone); +            anim.mNodes.add(nodeClone); +            anim.mNodeMap.put(nodeClone.animation, nodeClone); +            // Clear out the dependencies in the clone; we'll set these up manually later +            nodeClone.dependencies = null; +            nodeClone.tmpDependencies = null; +            nodeClone.nodeDependents = null; +            nodeClone.nodeDependencies = null; +            // clear out any listeners that were set up by the AnimatorSet; these will +            // be set up when the clone's nodes are sorted +            ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners(); +            if (cloneListeners != null) { +                ArrayList<AnimatorListener> listenersToRemove = null; +                for (AnimatorListener listener : cloneListeners) { +                    if (listener instanceof AnimatorSetListener) { +                        if (listenersToRemove == null) { +                            listenersToRemove = new ArrayList<AnimatorListener>(); +                        } +                        listenersToRemove.add(listener); +                    } +                } +                if (listenersToRemove != null) { +                    for (AnimatorListener listener : listenersToRemove) { +                        cloneListeners.remove(listener); +                    } +                } +            } +        } +        // Now that we've cloned all of the nodes, we're ready to walk through their +        // dependencies, mapping the old dependencies to the new nodes +        for (Node node : mNodes) { +            Node nodeClone = nodeCloneMap.get(node); +            if (node.dependencies != null) { +                for (Dependency dependency : node.dependencies) { +                    Node clonedDependencyNode = nodeCloneMap.get(dependency.node); +                    Dependency cloneDependency = new Dependency(clonedDependencyNode, +                            dependency.rule); +                    nodeClone.addDependency(cloneDependency); +                } +            } +        } + +        return anim; +    } + +    /** +     * This class is the mechanism by which animations are started based on events in other +     * animations. If an animation has multiple dependencies on other animations, then +     * all dependencies must be satisfied before the animation is started. +     */ +    private static class DependencyListener implements AnimatorListener { + +        private AnimatorSet mAnimatorSet; + +        // The node upon which the dependency is based. +        private Node mNode; + +        // The Dependency rule (WITH or AFTER) that the listener should wait for on +        // the node +        private int mRule; + +        public DependencyListener(AnimatorSet animatorSet, Node node, int rule) { +            this.mAnimatorSet = animatorSet; +            this.mNode = node; +            this.mRule = rule; +        } + +        /** +         * Ignore cancel events for now. We may want to handle this eventually, +         * to prevent follow-on animations from running when some dependency +         * animation is canceled. +         */ +        public void onAnimationCancel(Animator animation) { +        } + +        /** +         * An end event is received - see if this is an event we are listening for +         */ +        public void onAnimationEnd(Animator animation) { +            if (mRule == Dependency.AFTER) { +                startIfReady(animation); +            } +        } + +        /** +         * Ignore repeat events for now +         */ +        public void onAnimationRepeat(Animator animation) { +        } + +        /** +         * A start event is received - see if this is an event we are listening for +         */ +        public void onAnimationStart(Animator animation) { +            if (mRule == Dependency.WITH) { +                startIfReady(animation); +            } +        } + +        /** +         * Check whether the event received is one that the node was waiting for. +         * If so, mark it as complete and see whether it's time to start +         * the animation. +         * @param dependencyAnimation the animation that sent the event. +         */ +        private void startIfReady(Animator dependencyAnimation) { +            if (mAnimatorSet.mTerminated) { +                // if the parent AnimatorSet was canceled, then don't start any dependent anims +                return; +            } +            Dependency dependencyToRemove = null; +            int numDependencies = mNode.tmpDependencies.size(); +            for (int i = 0; i < numDependencies; ++i) { +                Dependency dependency = mNode.tmpDependencies.get(i); +                if (dependency.rule == mRule && +                        dependency.node.animation == dependencyAnimation) { +                    // rule fired - remove the dependency and listener and check to +                    // see whether it's time to start the animation +                    dependencyToRemove = dependency; +                    dependencyAnimation.removeListener(this); +                    break; +                } +            } +            mNode.tmpDependencies.remove(dependencyToRemove); +            if (mNode.tmpDependencies.size() == 0) { +                // all dependencies satisfied: start the animation +                mNode.animation.start(); +                mAnimatorSet.mPlayingSet.add(mNode.animation); +            } +        } + +    } + +    private class AnimatorSetListener implements AnimatorListener { + +        private AnimatorSet mAnimatorSet; + +        AnimatorSetListener(AnimatorSet animatorSet) { +            mAnimatorSet = animatorSet; +        } + +        public void onAnimationCancel(Animator animation) { +            if (!mTerminated) { +                // Listeners are already notified of the AnimatorSet canceling in cancel(). +                // The logic below only kicks in when animations end normally +                if (mPlayingSet.size() == 0) { +                    if (mListeners != null) { +                        int numListeners = mListeners.size(); +                        for (int i = 0; i < numListeners; ++i) { +                            mListeners.get(i).onAnimationCancel(mAnimatorSet); +                        } +                    } +                } +            } +        } + +        public void onAnimationEnd(Animator animation) { +            animation.removeListener(this); +            mPlayingSet.remove(animation); +            Node animNode = mAnimatorSet.mNodeMap.get(animation); +            animNode.done = true; +            if (!mTerminated) { +                // Listeners are already notified of the AnimatorSet ending in cancel() or +                // end(); the logic below only kicks in when animations end normally +                ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes; +                boolean allDone = true; +                int numSortedNodes = sortedNodes.size(); +                for (int i = 0; i < numSortedNodes; ++i) { +                    if (!sortedNodes.get(i).done) { +                        allDone = false; +                        break; +                    } +                } +                if (allDone) { +                    // If this was the last child animation to end, then notify listeners that this +                    // AnimatorSet has ended +                    if (mListeners != null) { +                        ArrayList<AnimatorListener> tmpListeners = +                                (ArrayList<AnimatorListener>) mListeners.clone(); +                        int numListeners = tmpListeners.size(); +                        for (int i = 0; i < numListeners; ++i) { +                            tmpListeners.get(i).onAnimationEnd(mAnimatorSet); +                        } +                    } +                    mAnimatorSet.mStarted = false; +                } +            } +        } + +        // Nothing to do +        public void onAnimationRepeat(Animator animation) { +        } + +        // Nothing to do +        public void onAnimationStart(Animator animation) { +        } + +    } + +    /** +     * This method sorts the current set of nodes, if needed. The sort is a simple +     * DependencyGraph sort, which goes like this: +     * - All nodes without dependencies become 'roots' +     * - while roots list is not null +     * -   for each root r +     * -     add r to sorted list +     * -     remove r as a dependency from any other node +     * -   any nodes with no dependencies are added to the roots list +     */ +    private void sortNodes() { +        if (mNeedsSort) { +            mSortedNodes.clear(); +            ArrayList<Node> roots = new ArrayList<Node>(); +            int numNodes = mNodes.size(); +            for (int i = 0; i < numNodes; ++i) { +                Node node = mNodes.get(i); +                if (node.dependencies == null || node.dependencies.size() == 0) { +                    roots.add(node); +                } +            } +            ArrayList<Node> tmpRoots = new ArrayList<Node>(); +            while (roots.size() > 0) { +                int numRoots = roots.size(); +                for (int i = 0; i < numRoots; ++i) { +                    Node root = roots.get(i); +                    mSortedNodes.add(root); +                    if (root.nodeDependents != null) { +                        int numDependents = root.nodeDependents.size(); +                        for (int j = 0; j < numDependents; ++j) { +                            Node node = root.nodeDependents.get(j); +                            node.nodeDependencies.remove(root); +                            if (node.nodeDependencies.size() == 0) { +                                tmpRoots.add(node); +                            } +                        } +                    } +                } +                roots.clear(); +                roots.addAll(tmpRoots); +                tmpRoots.clear(); +            } +            mNeedsSort = false; +            if (mSortedNodes.size() != mNodes.size()) { +                throw new IllegalStateException("Circular dependencies cannot exist" +                        + " in AnimatorSet"); +            } +        } else { +            // Doesn't need sorting, but still need to add in the nodeDependencies list +            // because these get removed as the event listeners fire and the dependencies +            // are satisfied +            int numNodes = mNodes.size(); +            for (int i = 0; i < numNodes; ++i) { +                Node node = mNodes.get(i); +                if (node.dependencies != null && node.dependencies.size() > 0) { +                    int numDependencies = node.dependencies.size(); +                    for (int j = 0; j < numDependencies; ++j) { +                        Dependency dependency = node.dependencies.get(j); +                        if (node.nodeDependencies == null) { +                            node.nodeDependencies = new ArrayList<Node>(); +                        } +                        if (!node.nodeDependencies.contains(dependency.node)) { +                            node.nodeDependencies.add(dependency.node); +                        } +                    } +                } +                // nodes are 'done' by default; they become un-done when started, and done +                // again when ended +                node.done = false; +            } +        } +    } + +    /** +     * Dependency holds information about the node that some other node is +     * dependent upon and the nature of that dependency. +     * +     */ +    private static class Dependency { +        static final int WITH = 0; // dependent node must start with this dependency node +        static final int AFTER = 1; // dependent node must start when this dependency node finishes + +        // The node that the other node with this Dependency is dependent upon +        public Node node; + +        // The nature of the dependency (WITH or AFTER) +        public int rule; + +        public Dependency(Node node, int rule) { +            this.node = node; +            this.rule = rule; +        } +    } + +    /** +     * A Node is an embodiment of both the Animator that it wraps as well as +     * any dependencies that are associated with that Animation. This includes +     * both dependencies upon other nodes (in the dependencies list) as +     * well as dependencies of other nodes upon this (in the nodeDependents list). +     */ +    private static class Node implements Cloneable { +        public Animator animation; + +        /** +         *  These are the dependencies that this node's animation has on other +         *  nodes. For example, if this node's animation should begin with some +         *  other animation ends, then there will be an item in this node's +         *  dependencies list for that other animation's node. +         */ +        public ArrayList<Dependency> dependencies = null; + +        /** +         * tmpDependencies is a runtime detail. We use the dependencies list for sorting. +         * But we also use the list to keep track of when multiple dependencies are satisfied, +         * but removing each dependency as it is satisfied. We do not want to remove +         * the dependency itself from the list, because we need to retain that information +         * if the AnimatorSet is launched in the future. So we create a copy of the dependency +         * list when the AnimatorSet starts and use this tmpDependencies list to track the +         * list of satisfied dependencies. +         */ +        public ArrayList<Dependency> tmpDependencies = null; + +        /** +         * nodeDependencies is just a list of the nodes that this Node is dependent upon. +         * This information is used in sortNodes(), to determine when a node is a root. +         */ +        public ArrayList<Node> nodeDependencies = null; + +        /** +         * nodeDepdendents is the list of nodes that have this node as a dependency. This +         * is a utility field used in sortNodes to facilitate removing this node as a +         * dependency when it is a root node. +         */ +        public ArrayList<Node> nodeDependents = null; + +        /** +         * Flag indicating whether the animation in this node is finished. This flag +         * is used by AnimatorSet to check, as each animation ends, whether all child animations +         * are done and it's time to send out an end event for the entire AnimatorSet. +         */ +        public boolean done = false; + +        /** +         * Constructs the Node with the animation that it encapsulates. A Node has no +         * dependencies by default; dependencies are added via the addDependency() +         * method. +         * +         * @param animation The animation that the Node encapsulates. +         */ +        public Node(Animator animation) { +            this.animation = animation; +        } + +        /** +         * Add a dependency to this Node. The dependency includes information about the +         * node that this node is dependency upon and the nature of the dependency. +         * @param dependency +         */ +        public void addDependency(Dependency dependency) { +            if (dependencies == null) { +                dependencies = new ArrayList<Dependency>(); +                nodeDependencies = new ArrayList<Node>(); +            } +            dependencies.add(dependency); +            if (!nodeDependencies.contains(dependency.node)) { +                nodeDependencies.add(dependency.node); +            } +            Node dependencyNode = dependency.node; +            if (dependencyNode.nodeDependents == null) { +                dependencyNode.nodeDependents = new ArrayList<Node>(); +            } +            dependencyNode.nodeDependents.add(this); +        } + +        @Override +        public Node clone() { +            try { +                Node node = (Node) super.clone(); +                node.animation = animation.clone(); +                return node; +            } catch (CloneNotSupportedException e) { +               throw new AssertionError(); +            } +        } +    } + +    /** +     * The <code>Builder</code> object is a utility class to facilitate adding animations to a +     * <code>AnimatorSet</code> along with the relationships between the various animations. The +     * intention of the <code>Builder</code> methods, along with the {@link +     * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible +     * to express the dependency relationships of animations in a natural way. Developers can also +     * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link +     * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need, +     * but it might be easier in some situations to express the AnimatorSet of animations in pairs. +     * <p/> +     * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed +     * internally via a call to {@link AnimatorSet#play(Animator)}.</p> +     * <p/> +     * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to +     * play when anim2 finishes, and anim4 to play when anim3 finishes:</p> +     * <pre> +     *     AnimatorSet s = new AnimatorSet(); +     *     s.play(anim1).with(anim2); +     *     s.play(anim2).before(anim3); +     *     s.play(anim4).after(anim3); +     * </pre> +     * <p/> +     * <p>Note in the example that both {@link Builder#before(Animator)} and {@link +     * Builder#after(Animator)} are used. These are just different ways of expressing the same +     * relationship and are provided to make it easier to say things in a way that is more natural, +     * depending on the situation.</p> +     * <p/> +     * <p>It is possible to make several calls into the same <code>Builder</code> object to express +     * multiple relationships. However, note that it is only the animation passed into the initial +     * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive +     * calls to the <code>Builder</code> object. For example, the following code starts both anim2 +     * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and +     * anim3: +     * <pre> +     *   AnimatorSet s = new AnimatorSet(); +     *   s.play(anim1).before(anim2).before(anim3); +     * </pre> +     * If the desired result is to play anim1 then anim2 then anim3, this code expresses the +     * relationship correctly:</p> +     * <pre> +     *   AnimatorSet s = new AnimatorSet(); +     *   s.play(anim1).before(anim2); +     *   s.play(anim2).before(anim3); +     * </pre> +     * <p/> +     * <p>Note that it is possible to express relationships that cannot be resolved and will not +     * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no +     * sense. In general, circular dependencies like this one (or more indirect ones where a depends +     * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets +     * that can boil down to a simple, one-way relationship of animations starting with, before, and +     * after other, different, animations.</p> +     */ +    public class Builder { + +        /** +         * This tracks the current node being processed. It is supplied to the play() method +         * of AnimatorSet and passed into the constructor of Builder. +         */ +        private Node mCurrentNode; + +        /** +         * package-private constructor. Builders are only constructed by AnimatorSet, when the +         * play() method is called. +         * +         * @param anim The animation that is the dependency for the other animations passed into +         * the other methods of this Builder object. +         */ +        Builder(Animator anim) { +            mCurrentNode = mNodeMap.get(anim); +            if (mCurrentNode == null) { +                mCurrentNode = new Node(anim); +                mNodeMap.put(anim, mCurrentNode); +                mNodes.add(mCurrentNode); +            } +        } + +        /** +         * Sets up the given animation to play at the same time as the animation supplied in the +         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object. +         * +         * @param anim The animation that will play when the animation supplied to the +         * {@link AnimatorSet#play(Animator)} method starts. +         */ +        public Builder with(Animator anim) { +            Node node = mNodeMap.get(anim); +            if (node == null) { +                node = new Node(anim); +                mNodeMap.put(anim, node); +                mNodes.add(node); +            } +            Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH); +            node.addDependency(dependency); +            return this; +        } + +        /** +         * Sets up the given animation to play when the animation supplied in the +         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object +         * ends. +         * +         * @param anim The animation that will play when the animation supplied to the +         * {@link AnimatorSet#play(Animator)} method ends. +         */ +        public Builder before(Animator anim) { +            Node node = mNodeMap.get(anim); +            if (node == null) { +                node = new Node(anim); +                mNodeMap.put(anim, node); +                mNodes.add(node); +            } +            Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER); +            node.addDependency(dependency); +            return this; +        } + +        /** +         * Sets up the given animation to play when the animation supplied in the +         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object +         * to start when the animation supplied in this method call ends. +         * +         * @param anim The animation whose end will cause the animation supplied to the +         * {@link AnimatorSet#play(Animator)} method to play. +         */ +        public Builder after(Animator anim) { +            Node node = mNodeMap.get(anim); +            if (node == null) { +                node = new Node(anim); +                mNodeMap.put(anim, node); +                mNodes.add(node); +            } +            Dependency dependency = new Dependency(node, Dependency.AFTER); +            mCurrentNode.addDependency(dependency); +            return this; +        } + +        /** +         * Sets up the animation supplied in the +         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object +         * to play when the given amount of time elapses. +         * +         * @param delay The number of milliseconds that should elapse before the +         * animation starts. +         */ +        public Builder after(long delay) { +            // setup dummy ValueAnimator just to run the clock +            ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); +            anim.setDuration(delay); +            after(anim); +            return this; +        } + +    } + +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatEvaluator.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatEvaluator.java new file mode 100644 index 000000000..e41019364 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatEvaluator.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +/** + * This evaluator can be used to perform type interpolation between <code>float</code> values. + */ +public class FloatEvaluator implements TypeEvaluator<Number> { + +    /** +     * This function returns the result of linearly interpolating the start and end values, with +     * <code>fraction</code> representing the proportion between the start and end values. The +     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>, +     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>, +     * and <code>t</code> is <code>fraction</code>. +     * +     * @param fraction   The fraction from the starting to the ending values +     * @param startValue The start value; should be of type <code>float</code> or +     *                   <code>Float</code> +     * @param endValue   The end value; should be of type <code>float</code> or <code>Float</code> +     * @return A linear interpolation between the start and end values, given the +     *         <code>fraction</code> parameter. +     */ +    public Float evaluate(float fraction, Number startValue, Number endValue) { +        float startFloat = startValue.floatValue(); +        return startFloat + fraction * (endValue.floatValue() - startFloat); +    } +}
\ No newline at end of file diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatKeyframeSet.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatKeyframeSet.java new file mode 100644 index 000000000..6d9dafa7a --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatKeyframeSet.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +import java.util.ArrayList; +import android.view.animation.Interpolator; + +import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.FloatKeyframe; + +/** + * This class holds a collection of FloatKeyframe objects and is called by ValueAnimator to calculate + * values between those keyframes for a given animation. The class internal to the animation + * package because it is an implementation detail of how Keyframes are stored and used. + * + * <p>This type-specific subclass of KeyframeSet, along with the other type-specific subclass for + * int, exists to speed up the getValue() method when there is no custom + * TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the + * Object equivalents of these primitive types.</p> + */ +@SuppressWarnings("unchecked") +class FloatKeyframeSet extends KeyframeSet { +    private float firstValue; +    private float lastValue; +    private float deltaValue; +    private boolean firstTime = true; + +    public FloatKeyframeSet(FloatKeyframe... keyframes) { +        super(keyframes); +    } + +    @Override +    public Object getValue(float fraction) { +        return getFloatValue(fraction); +    } + +    @Override +    public FloatKeyframeSet clone() { +        ArrayList<Keyframe> keyframes = mKeyframes; +        int numKeyframes = mKeyframes.size(); +        FloatKeyframe[] newKeyframes = new FloatKeyframe[numKeyframes]; +        for (int i = 0; i < numKeyframes; ++i) { +            newKeyframes[i] = (FloatKeyframe) keyframes.get(i).clone(); +        } +        FloatKeyframeSet newSet = new FloatKeyframeSet(newKeyframes); +        return newSet; +    } + +    public float getFloatValue(float fraction) { +        if (mNumKeyframes == 2) { +            if (firstTime) { +                firstTime = false; +                firstValue = ((FloatKeyframe) mKeyframes.get(0)).getFloatValue(); +                lastValue = ((FloatKeyframe) mKeyframes.get(1)).getFloatValue(); +                deltaValue = lastValue - firstValue; +            } +            if (mInterpolator != null) { +                fraction = mInterpolator.getInterpolation(fraction); +            } +            if (mEvaluator == null) { +                return firstValue + fraction * deltaValue; +            } else { +                return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).floatValue(); +            } +        } +        if (fraction <= 0f) { +            final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0); +            final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1); +            float prevValue = prevKeyframe.getFloatValue(); +            float nextValue = nextKeyframe.getFloatValue(); +            float prevFraction = prevKeyframe.getFraction(); +            float nextFraction = nextKeyframe.getFraction(); +            final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator(); +            if (interpolator != null) { +                fraction = interpolator.getInterpolation(fraction); +            } +            float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); +            return mEvaluator == null ? +                    prevValue + intervalFraction * (nextValue - prevValue) : +                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). +                            floatValue(); +        } else if (fraction >= 1f) { +            final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2); +            final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1); +            float prevValue = prevKeyframe.getFloatValue(); +            float nextValue = nextKeyframe.getFloatValue(); +            float prevFraction = prevKeyframe.getFraction(); +            float nextFraction = nextKeyframe.getFraction(); +            final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator(); +            if (interpolator != null) { +                fraction = interpolator.getInterpolation(fraction); +            } +            float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); +            return mEvaluator == null ? +                    prevValue + intervalFraction * (nextValue - prevValue) : +                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). +                            floatValue(); +        } +        FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0); +        for (int i = 1; i < mNumKeyframes; ++i) { +            FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i); +            if (fraction < nextKeyframe.getFraction()) { +                final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator(); +                if (interpolator != null) { +                    fraction = interpolator.getInterpolation(fraction); +                } +                float intervalFraction = (fraction - prevKeyframe.getFraction()) / +                    (nextKeyframe.getFraction() - prevKeyframe.getFraction()); +                float prevValue = prevKeyframe.getFloatValue(); +                float nextValue = nextKeyframe.getFloatValue(); +                return mEvaluator == null ? +                        prevValue + intervalFraction * (nextValue - prevValue) : +                        ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). +                            floatValue(); +            } +            prevKeyframe = nextKeyframe; +        } +        // shouldn't get here +        return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue(); +    } + +} + diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntEvaluator.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntEvaluator.java new file mode 100644 index 000000000..ed5e79ec6 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntEvaluator.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +/** + * This evaluator can be used to perform type interpolation between <code>int</code> values. + */ +public class IntEvaluator implements TypeEvaluator<Integer> { + +    /** +     * This function returns the result of linearly interpolating the start and end values, with +     * <code>fraction</code> representing the proportion between the start and end values. The +     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>, +     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>, +     * and <code>t</code> is <code>fraction</code>. +     * +     * @param fraction   The fraction from the starting to the ending values +     * @param startValue The start value; should be of type <code>int</code> or +     *                   <code>Integer</code> +     * @param endValue   The end value; should be of type <code>int</code> or <code>Integer</code> +     * @return A linear interpolation between the start and end values, given the +     *         <code>fraction</code> parameter. +     */ +    public Integer evaluate(float fraction, Integer startValue, Integer endValue) { +        int startInt = startValue; +        return (int)(startInt + fraction * (endValue - startInt)); +    } +}
\ No newline at end of file diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntKeyframeSet.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntKeyframeSet.java new file mode 100644 index 000000000..e9215e7f8 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntKeyframeSet.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +import java.util.ArrayList; +import android.view.animation.Interpolator; + +import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.IntKeyframe; + +/** + * This class holds a collection of IntKeyframe objects and is called by ValueAnimator to calculate + * values between those keyframes for a given animation. The class internal to the animation + * package because it is an implementation detail of how Keyframes are stored and used. + * + * <p>This type-specific subclass of KeyframeSet, along with the other type-specific subclass for + * float, exists to speed up the getValue() method when there is no custom + * TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the + * Object equivalents of these primitive types.</p> + */ +@SuppressWarnings("unchecked") +class IntKeyframeSet extends KeyframeSet { +    private int firstValue; +    private int lastValue; +    private int deltaValue; +    private boolean firstTime = true; + +    public IntKeyframeSet(IntKeyframe... keyframes) { +        super(keyframes); +    } + +    @Override +    public Object getValue(float fraction) { +        return getIntValue(fraction); +    } + +    @Override +    public IntKeyframeSet clone() { +        ArrayList<Keyframe> keyframes = mKeyframes; +        int numKeyframes = mKeyframes.size(); +        IntKeyframe[] newKeyframes = new IntKeyframe[numKeyframes]; +        for (int i = 0; i < numKeyframes; ++i) { +            newKeyframes[i] = (IntKeyframe) keyframes.get(i).clone(); +        } +        IntKeyframeSet newSet = new IntKeyframeSet(newKeyframes); +        return newSet; +    } + +    public int getIntValue(float fraction) { +        if (mNumKeyframes == 2) { +            if (firstTime) { +                firstTime = false; +                firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue(); +                lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue(); +                deltaValue = lastValue - firstValue; +            } +            if (mInterpolator != null) { +                fraction = mInterpolator.getInterpolation(fraction); +            } +            if (mEvaluator == null) { +                return firstValue + (int)(fraction * deltaValue); +            } else { +                return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue(); +            } +        } +        if (fraction <= 0f) { +            final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0); +            final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(1); +            int prevValue = prevKeyframe.getIntValue(); +            int nextValue = nextKeyframe.getIntValue(); +            float prevFraction = prevKeyframe.getFraction(); +            float nextFraction = nextKeyframe.getFraction(); +            final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator(); +            if (interpolator != null) { +                fraction = interpolator.getInterpolation(fraction); +            } +            float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); +            return mEvaluator == null ? +                    prevValue + (int)(intervalFraction * (nextValue - prevValue)) : +                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). +                            intValue(); +        } else if (fraction >= 1f) { +            final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 2); +            final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 1); +            int prevValue = prevKeyframe.getIntValue(); +            int nextValue = nextKeyframe.getIntValue(); +            float prevFraction = prevKeyframe.getFraction(); +            float nextFraction = nextKeyframe.getFraction(); +            final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator(); +            if (interpolator != null) { +                fraction = interpolator.getInterpolation(fraction); +            } +            float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); +            return mEvaluator == null ? +                    prevValue + (int)(intervalFraction * (nextValue - prevValue)) : +                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue(); +        } +        IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0); +        for (int i = 1; i < mNumKeyframes; ++i) { +            IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i); +            if (fraction < nextKeyframe.getFraction()) { +                final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator(); +                if (interpolator != null) { +                    fraction = interpolator.getInterpolation(fraction); +                } +                float intervalFraction = (fraction - prevKeyframe.getFraction()) / +                    (nextKeyframe.getFraction() - prevKeyframe.getFraction()); +                int prevValue = prevKeyframe.getIntValue(); +                int nextValue = nextKeyframe.getIntValue(); +                return mEvaluator == null ? +                        prevValue + (int)(intervalFraction * (nextValue - prevValue)) : +                        ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). +                                intValue(); +            } +            prevKeyframe = nextKeyframe; +        } +        // shouldn't get here +        return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).intValue(); +    } + +} + diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Keyframe.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Keyframe.java new file mode 100644 index 000000000..ab76fa7f6 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Keyframe.java @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +import android.view.animation.Interpolator; + +/** + * This class holds a time/value pair for an animation. The Keyframe class is used + * by {@link ValueAnimator} to define the values that the animation target will have over the course + * of the animation. As the time proceeds from one keyframe to the other, the value of the + * target object will animate between the value at the previous keyframe and the value at the + * next keyframe. Each keyframe also holds an optional {@link TimeInterpolator} + * object, which defines the time interpolation over the intervalue preceding the keyframe. + * + * <p>The Keyframe class itself is abstract. The type-specific factory methods will return + * a subclass of Keyframe specific to the type of value being stored. This is done to improve + * performance when dealing with the most common cases (e.g., <code>float</code> and + * <code>int</code> values). Other types will fall into a more general Keyframe class that + * treats its values as Objects. Unless your animation requires dealing with a custom type + * or a data structure that needs to be animated directly (and evaluated using an implementation + * of {@link TypeEvaluator}), you should stick to using float and int as animations using those + * types have lower runtime overhead than other types.</p> + */ +@SuppressWarnings("rawtypes") +public abstract class Keyframe implements Cloneable { +    /** +     * The time at which mValue will hold true. +     */ +    float mFraction; + +    /** +     * The type of the value in this Keyframe. This type is determined at construction time, +     * based on the type of the <code>value</code> object passed into the constructor. +     */ +    Class mValueType; + +    /** +     * The optional time interpolator for the interval preceding this keyframe. A null interpolator +     * (the default) results in linear interpolation over the interval. +     */ +    private /*Time*/Interpolator mInterpolator = null; + +    /** +     * Flag to indicate whether this keyframe has a valid value. This flag is used when an +     * animation first starts, to populate placeholder keyframes with real values derived +     * from the target object. +     */ +    boolean mHasValue = false; + +    /** +     * Constructs a Keyframe object with the given time and value. The time defines the +     * time, as a proportion of an overall animation's duration, at which the value will hold true +     * for the animation. The value for the animation between keyframes will be calculated as +     * an interpolation between the values at those keyframes. +     * +     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction +     * of time elapsed of the overall animation duration. +     * @param value The value that the object will animate to as the animation time approaches +     * the time in this keyframe, and the the value animated from as the time passes the time in +     * this keyframe. +     */ +    public static Keyframe ofInt(float fraction, int value) { +        return new IntKeyframe(fraction, value); +    } + +    /** +     * Constructs a Keyframe object with the given time. The value at this time will be derived +     * from the target object when the animation first starts (note that this implies that keyframes +     * with no initial value must be used as part of an {@link ObjectAnimator}). +     * The time defines the +     * time, as a proportion of an overall animation's duration, at which the value will hold true +     * for the animation. The value for the animation between keyframes will be calculated as +     * an interpolation between the values at those keyframes. +     * +     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction +     * of time elapsed of the overall animation duration. +     */ +    public static Keyframe ofInt(float fraction) { +        return new IntKeyframe(fraction); +    } + +    /** +     * Constructs a Keyframe object with the given time and value. The time defines the +     * time, as a proportion of an overall animation's duration, at which the value will hold true +     * for the animation. The value for the animation between keyframes will be calculated as +     * an interpolation between the values at those keyframes. +     * +     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction +     * of time elapsed of the overall animation duration. +     * @param value The value that the object will animate to as the animation time approaches +     * the time in this keyframe, and the the value animated from as the time passes the time in +     * this keyframe. +     */ +    public static Keyframe ofFloat(float fraction, float value) { +        return new FloatKeyframe(fraction, value); +    } + +    /** +     * Constructs a Keyframe object with the given time. The value at this time will be derived +     * from the target object when the animation first starts (note that this implies that keyframes +     * with no initial value must be used as part of an {@link ObjectAnimator}). +     * The time defines the +     * time, as a proportion of an overall animation's duration, at which the value will hold true +     * for the animation. The value for the animation between keyframes will be calculated as +     * an interpolation between the values at those keyframes. +     * +     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction +     * of time elapsed of the overall animation duration. +     */ +    public static Keyframe ofFloat(float fraction) { +        return new FloatKeyframe(fraction); +    } + +    /** +     * Constructs a Keyframe object with the given time and value. The time defines the +     * time, as a proportion of an overall animation's duration, at which the value will hold true +     * for the animation. The value for the animation between keyframes will be calculated as +     * an interpolation between the values at those keyframes. +     * +     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction +     * of time elapsed of the overall animation duration. +     * @param value The value that the object will animate to as the animation time approaches +     * the time in this keyframe, and the the value animated from as the time passes the time in +     * this keyframe. +     */ +    public static Keyframe ofObject(float fraction, Object value) { +        return new ObjectKeyframe(fraction, value); +    } + +    /** +     * Constructs a Keyframe object with the given time. The value at this time will be derived +     * from the target object when the animation first starts (note that this implies that keyframes +     * with no initial value must be used as part of an {@link ObjectAnimator}). +     * The time defines the +     * time, as a proportion of an overall animation's duration, at which the value will hold true +     * for the animation. The value for the animation between keyframes will be calculated as +     * an interpolation between the values at those keyframes. +     * +     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction +     * of time elapsed of the overall animation duration. +     */ +    public static Keyframe ofObject(float fraction) { +        return new ObjectKeyframe(fraction, null); +    } + +    /** +     * Indicates whether this keyframe has a valid value. This method is called internally when +     * an {@link ObjectAnimator} first starts; keyframes without values are assigned values at +     * that time by deriving the value for the property from the target object. +     * +     * @return boolean Whether this object has a value assigned. +     */ +    public boolean hasValue() { +        return mHasValue; +    } + +    /** +     * Gets the value for this Keyframe. +     * +     * @return The value for this Keyframe. +     */ +    public abstract Object getValue(); + +    /** +     * Sets the value for this Keyframe. +     * +     * @param value value for this Keyframe. +     */ +    public abstract void setValue(Object value); + +    /** +     * Gets the time for this keyframe, as a fraction of the overall animation duration. +     * +     * @return The time associated with this keyframe, as a fraction of the overall animation +     * duration. This should be a value between 0 and 1. +     */ +    public float getFraction() { +        return mFraction; +    } + +    /** +     * Sets the time for this keyframe, as a fraction of the overall animation duration. +     * +     * @param fraction time associated with this keyframe, as a fraction of the overall animation +     * duration. This should be a value between 0 and 1. +     */ +    public void setFraction(float fraction) { +        mFraction = fraction; +    } + +    /** +     * Gets the optional interpolator for this Keyframe. A value of <code>null</code> indicates +     * that there is no interpolation, which is the same as linear interpolation. +     * +     * @return The optional interpolator for this Keyframe. +     */ +    public /*Time*/Interpolator getInterpolator() { +        return mInterpolator; +    } + +    /** +     * Sets the optional interpolator for this Keyframe. A value of <code>null</code> indicates +     * that there is no interpolation, which is the same as linear interpolation. +     * +     * @return The optional interpolator for this Keyframe. +     */ +    public void setInterpolator(/*Time*/Interpolator interpolator) { +        mInterpolator = interpolator; +    } + +    /** +     * Gets the type of keyframe. This information is used by ValueAnimator to determine the type of +     * {@link TypeEvaluator} to use when calculating values between keyframes. The type is based +     * on the type of Keyframe created. +     * +     * @return The type of the value stored in the Keyframe. +     */ +    public Class getType() { +        return mValueType; +    } + +    @Override +    public abstract Keyframe clone(); + +    /** +     * This internal subclass is used for all types which are not int or float. +     */ +    static class ObjectKeyframe extends Keyframe { + +        /** +         * The value of the animation at the time mFraction. +         */ +        Object mValue; + +        ObjectKeyframe(float fraction, Object value) { +            mFraction = fraction; +            mValue = value; +            mHasValue = (value != null); +            mValueType = mHasValue ? value.getClass() : Object.class; +        } + +        public Object getValue() { +            return mValue; +        } + +        public void setValue(Object value) { +            mValue = value; +            mHasValue = (value != null); +        } + +        @Override +        public ObjectKeyframe clone() { +            ObjectKeyframe kfClone = new ObjectKeyframe(getFraction(), mValue); +            kfClone.setInterpolator(getInterpolator()); +            return kfClone; +        } +    } + +    /** +     * Internal subclass used when the keyframe value is of type int. +     */ +    static class IntKeyframe extends Keyframe { + +        /** +         * The value of the animation at the time mFraction. +         */ +        int mValue; + +        IntKeyframe(float fraction, int value) { +            mFraction = fraction; +            mValue = value; +            mValueType = int.class; +            mHasValue = true; +        } + +        IntKeyframe(float fraction) { +            mFraction = fraction; +            mValueType = int.class; +        } + +        public int getIntValue() { +            return mValue; +        } + +        public Object getValue() { +            return mValue; +        } + +        public void setValue(Object value) { +            if (value != null && value.getClass() == Integer.class) { +                mValue = ((Integer)value).intValue(); +                mHasValue = true; +            } +        } + +        @Override +        public IntKeyframe clone() { +            IntKeyframe kfClone = new IntKeyframe(getFraction(), mValue); +            kfClone.setInterpolator(getInterpolator()); +            return kfClone; +        } +    } + +    /** +     * Internal subclass used when the keyframe value is of type float. +     */ +    static class FloatKeyframe extends Keyframe { +        /** +         * The value of the animation at the time mFraction. +         */ +        float mValue; + +        FloatKeyframe(float fraction, float value) { +            mFraction = fraction; +            mValue = value; +            mValueType = float.class; +            mHasValue = true; +        } + +        FloatKeyframe(float fraction) { +            mFraction = fraction; +            mValueType = float.class; +        } + +        public float getFloatValue() { +            return mValue; +        } + +        public Object getValue() { +            return mValue; +        } + +        public void setValue(Object value) { +            if (value != null && value.getClass() == Float.class) { +                mValue = ((Float)value).floatValue(); +                mHasValue = true; +            } +        } + +        @Override +        public FloatKeyframe clone() { +            FloatKeyframe kfClone = new FloatKeyframe(getFraction(), mValue); +            kfClone.setInterpolator(getInterpolator()); +            return kfClone; +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/KeyframeSet.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/KeyframeSet.java new file mode 100644 index 000000000..a71e1ad3c --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/KeyframeSet.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +import java.util.ArrayList; +import java.util.Arrays; +import android.view.animation.Interpolator; + +import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.FloatKeyframe; +import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.IntKeyframe; +import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.ObjectKeyframe; + +/** + * This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate + * values between those keyframes for a given animation. The class internal to the animation + * package because it is an implementation detail of how Keyframes are stored and used. + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +class KeyframeSet { + +    int mNumKeyframes; + +    Keyframe mFirstKeyframe; +    Keyframe mLastKeyframe; +    /*Time*/Interpolator mInterpolator; // only used in the 2-keyframe case +    ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes +    TypeEvaluator mEvaluator; + + +    public KeyframeSet(Keyframe... keyframes) { +        mNumKeyframes = keyframes.length; +        mKeyframes = new ArrayList<Keyframe>(); +        mKeyframes.addAll(Arrays.asList(keyframes)); +        mFirstKeyframe = mKeyframes.get(0); +        mLastKeyframe = mKeyframes.get(mNumKeyframes - 1); +        mInterpolator = mLastKeyframe.getInterpolator(); +    } + +    public static KeyframeSet ofInt(int... values) { +        int numKeyframes = values.length; +        IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)]; +        if (numKeyframes == 1) { +            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f); +            keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]); +        } else { +            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]); +            for (int i = 1; i < numKeyframes; ++i) { +                keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]); +            } +        } +        return new IntKeyframeSet(keyframes); +    } + +    public static KeyframeSet ofFloat(float... values) { +        int numKeyframes = values.length; +        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)]; +        if (numKeyframes == 1) { +            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f); +            keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]); +        } else { +            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]); +            for (int i = 1; i < numKeyframes; ++i) { +                keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]); +            } +        } +        return new FloatKeyframeSet(keyframes); +    } + +    public static KeyframeSet ofKeyframe(Keyframe... keyframes) { +        // if all keyframes of same primitive type, create the appropriate KeyframeSet +        int numKeyframes = keyframes.length; +        boolean hasFloat = false; +        boolean hasInt = false; +        boolean hasOther = false; +        for (int i = 0; i < numKeyframes; ++i) { +            if (keyframes[i] instanceof FloatKeyframe) { +                hasFloat = true; +            } else if (keyframes[i] instanceof IntKeyframe) { +                hasInt = true; +            } else { +                hasOther = true; +            } +        } +        if (hasFloat && !hasInt && !hasOther) { +            FloatKeyframe floatKeyframes[] = new FloatKeyframe[numKeyframes]; +            for (int i = 0; i < numKeyframes; ++i) { +                floatKeyframes[i] = (FloatKeyframe) keyframes[i]; +            } +            return new FloatKeyframeSet(floatKeyframes); +        } else if (hasInt && !hasFloat && !hasOther) { +            IntKeyframe intKeyframes[] = new IntKeyframe[numKeyframes]; +            for (int i = 0; i < numKeyframes; ++i) { +                intKeyframes[i] = (IntKeyframe) keyframes[i]; +            } +            return new IntKeyframeSet(intKeyframes); +        } else { +            return new KeyframeSet(keyframes); +        } +    } + +    public static KeyframeSet ofObject(Object... values) { +        int numKeyframes = values.length; +        ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,2)]; +        if (numKeyframes == 1) { +            keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f); +            keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]); +        } else { +            keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]); +            for (int i = 1; i < numKeyframes; ++i) { +                keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]); +            } +        } +        return new KeyframeSet(keyframes); +    } + +    /** +     * Sets the TypeEvaluator to be used when calculating animated values. This object +     * is required only for KeyframeSets that are not either IntKeyframeSet or FloatKeyframeSet, +     * both of which assume their own evaluator to speed up calculations with those primitive +     * types. +     * +     * @param evaluator The TypeEvaluator to be used to calculate animated values. +     */ +    public void setEvaluator(TypeEvaluator evaluator) { +        mEvaluator = evaluator; +    } + +    @Override +    public KeyframeSet clone() { +        ArrayList<Keyframe> keyframes = mKeyframes; +        int numKeyframes = mKeyframes.size(); +        Keyframe[] newKeyframes = new Keyframe[numKeyframes]; +        for (int i = 0; i < numKeyframes; ++i) { +            newKeyframes[i] = keyframes.get(i).clone(); +        } +        KeyframeSet newSet = new KeyframeSet(newKeyframes); +        return newSet; +    } + +    /** +     * Gets the animated value, given the elapsed fraction of the animation (interpolated by the +     * animation's interpolator) and the evaluator used to calculate in-between values. This +     * function maps the input fraction to the appropriate keyframe interval and a fraction +     * between them and returns the interpolated value. Note that the input fraction may fall +     * outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a +     * spring interpolation that might send the fraction past 1.0). We handle this situation by +     * just using the two keyframes at the appropriate end when the value is outside those bounds. +     * +     * @param fraction The elapsed fraction of the animation +     * @return The animated value. +     */ +    public Object getValue(float fraction) { + +        // Special-case optimization for the common case of only two keyframes +        if (mNumKeyframes == 2) { +            if (mInterpolator != null) { +                fraction = mInterpolator.getInterpolation(fraction); +            } +            return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(), +                    mLastKeyframe.getValue()); +        } +        if (fraction <= 0f) { +            final Keyframe nextKeyframe = mKeyframes.get(1); +            final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator(); +            if (interpolator != null) { +                fraction = interpolator.getInterpolation(fraction); +            } +            final float prevFraction = mFirstKeyframe.getFraction(); +            float intervalFraction = (fraction - prevFraction) / +                (nextKeyframe.getFraction() - prevFraction); +            return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(), +                    nextKeyframe.getValue()); +        } else if (fraction >= 1f) { +            final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2); +            final /*Time*/Interpolator interpolator = mLastKeyframe.getInterpolator(); +            if (interpolator != null) { +                fraction = interpolator.getInterpolation(fraction); +            } +            final float prevFraction = prevKeyframe.getFraction(); +            float intervalFraction = (fraction - prevFraction) / +                (mLastKeyframe.getFraction() - prevFraction); +            return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(), +                    mLastKeyframe.getValue()); +        } +        Keyframe prevKeyframe = mFirstKeyframe; +        for (int i = 1; i < mNumKeyframes; ++i) { +            Keyframe nextKeyframe = mKeyframes.get(i); +            if (fraction < nextKeyframe.getFraction()) { +                final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator(); +                if (interpolator != null) { +                    fraction = interpolator.getInterpolation(fraction); +                } +                final float prevFraction = prevKeyframe.getFraction(); +                float intervalFraction = (fraction - prevFraction) / +                    (nextKeyframe.getFraction() - prevFraction); +                return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(), +                        nextKeyframe.getValue()); +            } +            prevKeyframe = nextKeyframe; +        } +        // shouldn't reach here +        return mLastKeyframe.getValue(); +    } + +    @Override +    public String toString() { +        String returnVal = " "; +        for (int i = 0; i < mNumKeyframes; ++i) { +            returnVal += mKeyframes.get(i).getValue() + "  "; +        } +        return returnVal; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ObjectAnimator.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ObjectAnimator.java new file mode 100644 index 000000000..21d15c02a --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ObjectAnimator.java @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +import android.util.Log; +//import android.util.Property; + +//import java.lang.reflect.Method; +import java.util.ArrayList; + +/** + * This subclass of {@link ValueAnimator} provides support for animating properties on target objects. + * The constructors of this class take parameters to define the target object that will be animated + * as well as the name of the property that will be animated. Appropriate set/get functions + * are then determined internally and the animation will call these functions as necessary to + * animate the property. + * + * @see #setPropertyName(String) + * + */ +@SuppressWarnings("rawtypes") +public final class ObjectAnimator extends ValueAnimator { +    private static final boolean DBG = false; + +    // The target object on which the property exists, set in the constructor +    private Object mTarget; + +    private String mPropertyName; + +    //private Property mProperty; + +    /** +     * Sets the name of the property that will be animated. This name is used to derive +     * a setter function that will be called to set animated values. +     * For example, a property name of <code>foo</code> will result +     * in a call to the function <code>setFoo()</code> on the target object. If either +     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will +     * also be derived and called. +     * +     * <p>For best performance of the mechanism that calls the setter function determined by the +     * name of the property being animated, use <code>float</code> or <code>int</code> typed values, +     * and make the setter function for those properties have a <code>void</code> return value. This +     * will cause the code to take an optimized path for these constrained circumstances. Other +     * property types and return types will work, but will have more overhead in processing +     * the requests due to normal reflection mechanisms.</p> +     * +     * <p>Note that the setter function derived from this property name +     * must take the same parameter type as the +     * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to +     * the setter function will fail.</p> +     * +     * <p>If this ObjectAnimator has been set up to animate several properties together, +     * using more than one PropertyValuesHolder objects, then setting the propertyName simply +     * sets the propertyName in the first of those PropertyValuesHolder objects.</p> +     * +     * @param propertyName The name of the property being animated. Should not be null. +     */ +    public void setPropertyName(String propertyName) { +        // mValues could be null if this is being constructed piecemeal. Just record the +        // propertyName to be used later when setValues() is called if so. +        if (mValues != null) { +            PropertyValuesHolder valuesHolder = mValues[0]; +            String oldName = valuesHolder.getPropertyName(); +            valuesHolder.setPropertyName(propertyName); +            mValuesMap.remove(oldName); +            mValuesMap.put(propertyName, valuesHolder); +        } +        mPropertyName = propertyName; +        // New property/values/target should cause re-initialization prior to starting +        mInitialized = false; +    } + +    /** +     * Sets the property that will be animated. Property objects will take precedence over +     * properties specified by the {@link #setPropertyName(String)} method. Animations should +     * be set up to use one or the other, not both. +     * +     * @param property The property being animated. Should not be null. +     */ +    //public void setProperty(Property property) { +    //    // mValues could be null if this is being constructed piecemeal. Just record the +    //    // propertyName to be used later when setValues() is called if so. +    //    if (mValues != null) { +    //        PropertyValuesHolder valuesHolder = mValues[0]; +    //        String oldName = valuesHolder.getPropertyName(); +    //        valuesHolder.setProperty(property); +    //        mValuesMap.remove(oldName); +    //        mValuesMap.put(mPropertyName, valuesHolder); +    //    } +    //    if (mProperty != null) { +    //        mPropertyName = property.getName(); +    //    } +    //    mProperty = property; +    //    // New property/values/target should cause re-initialization prior to starting +    //    mInitialized = false; +    //} + +    /** +     * Gets the name of the property that will be animated. This name will be used to derive +     * a setter function that will be called to set animated values. +     * For example, a property name of <code>foo</code> will result +     * in a call to the function <code>setFoo()</code> on the target object. If either +     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will +     * also be derived and called. +     */ +    public String getPropertyName() { +        return mPropertyName; +    } + +    /** +     * Creates a new ObjectAnimator object. This default constructor is primarily for +     * use internally; the other constructors which take parameters are more generally +     * useful. +     */ +    public ObjectAnimator() { +    } + +    /** +     * Private utility constructor that initializes the target object and name of the +     * property being animated. +     * +     * @param target The object whose property is to be animated. This object should +     * have a public method on it called <code>setName()</code>, where <code>name</code> is +     * the value of the <code>propertyName</code> parameter. +     * @param propertyName The name of the property being animated. +     */ +    private ObjectAnimator(Object target, String propertyName) { +        mTarget = target; +        setPropertyName(propertyName); +    } + +    /** +     * Private utility constructor that initializes the target object and property being animated. +     * +     * @param target The object whose property is to be animated. +     * @param property The property being animated. +     */ +    //private <T> ObjectAnimator(T target, Property<T, ?> property) { +    //    mTarget = target; +    //    setProperty(property); +    //} + +    /** +     * Constructs and returns an ObjectAnimator that animates between int values. A single +     * value implies that that value is the one being animated to. Two values imply a starting +     * and ending values. More than two values imply a starting value, values to animate through +     * along the way, and an ending value (these values will be distributed evenly across +     * the duration of the animation). +     * +     * @param target The object whose property is to be animated. This object should +     * have a public method on it called <code>setName()</code>, where <code>name</code> is +     * the value of the <code>propertyName</code> parameter. +     * @param propertyName The name of the property being animated. +     * @param values A set of values that the animation will animate between over time. +     * @return An ObjectAnimator object that is set up to animate between the given values. +     */ +    public static ObjectAnimator ofInt(Object target, String propertyName, int... values) { +        ObjectAnimator anim = new ObjectAnimator(target, propertyName); +        anim.setIntValues(values); +        return anim; +    } + +    /** +     * Constructs and returns an ObjectAnimator that animates between int values. A single +     * value implies that that value is the one being animated to. Two values imply a starting +     * and ending values. More than two values imply a starting value, values to animate through +     * along the way, and an ending value (these values will be distributed evenly across +     * the duration of the animation). +     * +     * @param target The object whose property is to be animated. +     * @param property The property being animated. +     * @param values A set of values that the animation will animate between over time. +     * @return An ObjectAnimator object that is set up to animate between the given values. +     */ +    //public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) { +    //    ObjectAnimator anim = new ObjectAnimator(target, property); +    //    anim.setIntValues(values); +    //    return anim; +    //} + +    /** +     * Constructs and returns an ObjectAnimator that animates between float values. A single +     * value implies that that value is the one being animated to. Two values imply a starting +     * and ending values. More than two values imply a starting value, values to animate through +     * along the way, and an ending value (these values will be distributed evenly across +     * the duration of the animation). +     * +     * @param target The object whose property is to be animated. This object should +     * have a public method on it called <code>setName()</code>, where <code>name</code> is +     * the value of the <code>propertyName</code> parameter. +     * @param propertyName The name of the property being animated. +     * @param values A set of values that the animation will animate between over time. +     * @return An ObjectAnimator object that is set up to animate between the given values. +     */ +    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { +        ObjectAnimator anim = new ObjectAnimator(target, propertyName); +        anim.setFloatValues(values); +        return anim; +    } + +    /** +     * Constructs and returns an ObjectAnimator that animates between float values. A single +     * value implies that that value is the one being animated to. Two values imply a starting +     * and ending values. More than two values imply a starting value, values to animate through +     * along the way, and an ending value (these values will be distributed evenly across +     * the duration of the animation). +     * +     * @param target The object whose property is to be animated. +     * @param property The property being animated. +     * @param values A set of values that the animation will animate between over time. +     * @return An ObjectAnimator object that is set up to animate between the given values. +     */ +    //public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property, +    //        float... values) { +    //    ObjectAnimator anim = new ObjectAnimator(target, property); +    //    anim.setFloatValues(values); +    //    return anim; +    //} + +    /** +     * Constructs and returns an ObjectAnimator that animates between Object values. A single +     * value implies that that value is the one being animated to. Two values imply a starting +     * and ending values. More than two values imply a starting value, values to animate through +     * along the way, and an ending value (these values will be distributed evenly across +     * the duration of the animation). +     * +     * @param target The object whose property is to be animated. This object should +     * have a public method on it called <code>setName()</code>, where <code>name</code> is +     * the value of the <code>propertyName</code> parameter. +     * @param propertyName The name of the property being animated. +     * @param evaluator A TypeEvaluator that will be called on each animation frame to +     * provide the necessary interpolation between the Object values to derive the animated +     * value. +     * @param values A set of values that the animation will animate between over time. +     * @return An ObjectAnimator object that is set up to animate between the given values. +     */ +    public static ObjectAnimator ofObject(Object target, String propertyName, +            TypeEvaluator evaluator, Object... values) { +        ObjectAnimator anim = new ObjectAnimator(target, propertyName); +        anim.setObjectValues(values); +        anim.setEvaluator(evaluator); +        return anim; +    } + +    /** +     * Constructs and returns an ObjectAnimator that animates between Object values. A single +     * value implies that that value is the one being animated to. Two values imply a starting +     * and ending values. More than two values imply a starting value, values to animate through +     * along the way, and an ending value (these values will be distributed evenly across +     * the duration of the animation). +     * +     * @param target The object whose property is to be animated. +     * @param property The property being animated. +     * @param evaluator A TypeEvaluator that will be called on each animation frame to +     * provide the necessary interpolation between the Object values to derive the animated +     * value. +     * @param values A set of values that the animation will animate between over time. +     * @return An ObjectAnimator object that is set up to animate between the given values. +     */ +    //public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property, +    //        TypeEvaluator<V> evaluator, V... values) { +    //    ObjectAnimator anim = new ObjectAnimator(target, property); +    //    anim.setObjectValues(values); +    //    anim.setEvaluator(evaluator); +    //    return anim; +    //} + +    /** +     * Constructs and returns an ObjectAnimator that animates between the sets of values specified +     * in <code>PropertyValueHolder</code> objects. This variant should be used when animating +     * several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows +     * you to associate a set of animation values with a property name. +     * +     * @param target The object whose property is to be animated. Depending on how the +     * PropertyValuesObjects were constructed, the target object should either have the {@link +     * android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the +     * PropertyValuesHOlder objects were created with property names) the target object should have +     * public methods on it called <code>setName()</code>, where <code>name</code> is the name of +     * the property passed in as the <code>propertyName</code> parameter for each of the +     * PropertyValuesHolder objects. +     * @param values A set of PropertyValuesHolder objects whose values will be animated between +     * over time. +     * @return An ObjectAnimator object that is set up to animate between the given values. +     */ +    public static ObjectAnimator ofPropertyValuesHolder(Object target, +            PropertyValuesHolder... values) { +        ObjectAnimator anim = new ObjectAnimator(); +        anim.mTarget = target; +        anim.setValues(values); +        return anim; +    } + +    @Override +    public void setIntValues(int... values) { +        if (mValues == null || mValues.length == 0) { +            // No values yet - this animator is being constructed piecemeal. Init the values with +            // whatever the current propertyName is +            //if (mProperty != null) { +            //    setValues(PropertyValuesHolder.ofInt(mProperty, values)); +            //} else { +                setValues(PropertyValuesHolder.ofInt(mPropertyName, values)); +            //} +        } else { +            super.setIntValues(values); +        } +    } + +    @Override +    public void setFloatValues(float... values) { +        if (mValues == null || mValues.length == 0) { +            // No values yet - this animator is being constructed piecemeal. Init the values with +            // whatever the current propertyName is +            //if (mProperty != null) { +            //    setValues(PropertyValuesHolder.ofFloat(mProperty, values)); +            //} else { +                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values)); +            //} +        } else { +            super.setFloatValues(values); +        } +    } + +    @Override +    public void setObjectValues(Object... values) { +        if (mValues == null || mValues.length == 0) { +            // No values yet - this animator is being constructed piecemeal. Init the values with +            // whatever the current propertyName is +            //if (mProperty != null) { +            //    setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator)null, values)); +            //} else { +                setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values)); +            //} +        } else { +            super.setObjectValues(values); +        } +    } + +    @Override +    public void start() { +        if (DBG) { +            Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration()); +            for (int i = 0; i < mValues.length; ++i) { +                PropertyValuesHolder pvh = mValues[i]; +                ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes; +                Log.d("ObjectAnimator", "   Values[" + i + "]: " + +                    pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " + +                    keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue()); +            } +        } +        super.start(); +    } + +    /** +     * This function is called immediately before processing the first animation +     * frame of an animation. If there is a nonzero <code>startDelay</code>, the +     * function is called after that delay ends. +     * It takes care of the final initialization steps for the +     * animation. This includes setting mEvaluator, if the user has not yet +     * set it up, and the setter/getter methods, if the user did not supply +     * them. +     * +     *  <p>Overriders of this method should call the superclass method to cause +     *  internal mechanisms to be set up correctly.</p> +     */ +    @Override +    void initAnimation() { +        if (!mInitialized) { +            // mValueType may change due to setter/getter setup; do this before calling super.init(), +            // which uses mValueType to set up the default type evaluator. +            int numValues = mValues.length; +            for (int i = 0; i < numValues; ++i) { +                mValues[i].setupSetterAndGetter(mTarget); +            } +            super.initAnimation(); +        } +    } + +    /** +     * Sets the length of the animation. The default duration is 300 milliseconds. +     * +     * @param duration The length of the animation, in milliseconds. +     * @return ObjectAnimator The object called with setDuration(). This return +     * value makes it easier to compose statements together that construct and then set the +     * duration, as in +     * <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>. +     */ +    @Override +    public ObjectAnimator setDuration(long duration) { +        super.setDuration(duration); +        return this; +    } + + +    /** +     * The target object whose property will be animated by this animation +     * +     * @return The object being animated +     */ +    public Object getTarget() { +        return mTarget; +    } + +    /** +     * Sets the target object whose property will be animated by this animation +     * +     * @param target The object being animated +     */ +    @Override +    public void setTarget(Object target) { +        if (mTarget != target) { +            final Object oldTarget = mTarget; +            mTarget = target; +            if (oldTarget != null && target != null && oldTarget.getClass() == target.getClass()) { +                return; +            } +            // New target type should cause re-initialization prior to starting +            mInitialized = false; +        } +    } + +    @Override +    public void setupStartValues() { +        initAnimation(); +        int numValues = mValues.length; +        for (int i = 0; i < numValues; ++i) { +            mValues[i].setupStartValue(mTarget); +        } +    } + +    @Override +    public void setupEndValues() { +        initAnimation(); +        int numValues = mValues.length; +        for (int i = 0; i < numValues; ++i) { +            mValues[i].setupEndValue(mTarget); +        } +    } + +    /** +     * This method is called with the elapsed fraction of the animation during every +     * animation frame. This function turns the elapsed fraction into an interpolated fraction +     * and then into an animated value (from the evaluator. The function is called mostly during +     * animation updates, but it is also called when the <code>end()</code> +     * function is called, to set the final value on the property. +     * +     * <p>Overrides of this method must call the superclass to perform the calculation +     * of the animated value.</p> +     * +     * @param fraction The elapsed fraction of the animation. +     */ +    @Override +    void animateValue(float fraction) { +        super.animateValue(fraction); +        int numValues = mValues.length; +        for (int i = 0; i < numValues; ++i) { +            mValues[i].setAnimatedValue(mTarget); +        } +    } + +    @Override +    public ObjectAnimator clone() { +        final ObjectAnimator anim = (ObjectAnimator) super.clone(); +        return anim; +    } + +    @Override +    public String toString() { +        String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " + +            mTarget; +        if (mValues != null) { +            for (int i = 0; i < mValues.length; ++i) { +                returnVal += "\n    " + mValues[i].toString(); +            } +        } +        return returnVal; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java new file mode 100644 index 000000000..84f7504ab --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java @@ -0,0 +1,1012 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +//import android.util.FloatProperty; +//import android.util.IntProperty; +import android.util.Log; +//import android.util.Property; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * This class holds information about a property and the values that that property + * should take on during an animation. PropertyValuesHolder objects can be used to create + * animations with ValueAnimator or ObjectAnimator that operate on several different properties + * in parallel. + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +public class PropertyValuesHolder implements Cloneable { + +    /** +     * The name of the property associated with the values. This need not be a real property, +     * unless this object is being used with ObjectAnimator. But this is the name by which +     * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator. +     */ +    String mPropertyName; + +    /** +     * @hide +     */ +    //protected Property mProperty; + +    /** +     * The setter function, if needed. ObjectAnimator hands off this functionality to +     * PropertyValuesHolder, since it holds all of the per-property information. This +     * property is automatically +     * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator. +     */ +    Method mSetter = null; + +    /** +     * The getter function, if needed. ObjectAnimator hands off this functionality to +     * PropertyValuesHolder, since it holds all of the per-property information. This +     * property is automatically +     * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator. +     * The getter is only derived and used if one of the values is null. +     */ +    private Method mGetter = null; + +    /** +     * The type of values supplied. This information is used both in deriving the setter/getter +     * functions and in deriving the type of TypeEvaluator. +     */ +    Class mValueType; + +    /** +     * The set of keyframes (time/value pairs) that define this animation. +     */ +    KeyframeSet mKeyframeSet = null; + + +    // type evaluators for the primitive types handled by this implementation +    private static final TypeEvaluator sIntEvaluator = new IntEvaluator(); +    private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator(); + +    // We try several different types when searching for appropriate setter/getter functions. +    // The caller may have supplied values in a type that does not match the setter/getter +    // functions (such as the integers 0 and 1 to represent floating point values for alpha). +    // Also, the use of generics in constructors means that we end up with the Object versions +    // of primitive types (Float vs. float). But most likely, the setter/getter functions +    // will take primitive types instead. +    // So we supply an ordered array of other types to try before giving up. +    private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class, +            Double.class, Integer.class}; +    private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class, +            Float.class, Double.class}; +    private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class, +            Float.class, Integer.class}; + +    // These maps hold all property entries for a particular class. This map +    // is used to speed up property/setter/getter lookups for a given class/property +    // combination. No need to use reflection on the combination more than once. +    private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap = +            new HashMap<Class, HashMap<String, Method>>(); +    private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap = +            new HashMap<Class, HashMap<String, Method>>(); + +    // This lock is used to ensure that only one thread is accessing the property maps +    // at a time. +    final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock(); + +    // Used to pass single value to varargs parameter in setter invocation +    final Object[] mTmpValueArray = new Object[1]; + +    /** +     * The type evaluator used to calculate the animated values. This evaluator is determined +     * automatically based on the type of the start/end objects passed into the constructor, +     * but the system only knows about the primitive types int and float. Any other +     * type will need to set the evaluator to a custom evaluator for that type. +     */ +    private TypeEvaluator mEvaluator; + +    /** +     * The value most recently calculated by calculateValue(). This is set during +     * that function and might be retrieved later either by ValueAnimator.animatedValue() or +     * by the property-setting logic in ObjectAnimator.animatedValue(). +     */ +    private Object mAnimatedValue; + +    /** +     * Internal utility constructor, used by the factory methods to set the property name. +     * @param propertyName The name of the property for this holder. +     */ +    private PropertyValuesHolder(String propertyName) { +        mPropertyName = propertyName; +    } + +    /** +     * Internal utility constructor, used by the factory methods to set the property. +     * @param property The property for this holder. +     */ +    //private PropertyValuesHolder(Property property) { +    //    mProperty = property; +    //    if (property != null) { +    //        mPropertyName = property.getName(); +    //    } +    //} + +    /** +     * Constructs and returns a PropertyValuesHolder with a given property name and +     * set of int values. +     * @param propertyName The name of the property being animated. +     * @param values The values that the named property will animate between. +     * @return PropertyValuesHolder The constructed PropertyValuesHolder object. +     */ +    public static PropertyValuesHolder ofInt(String propertyName, int... values) { +        return new IntPropertyValuesHolder(propertyName, values); +    } + +    /** +     * Constructs and returns a PropertyValuesHolder with a given property and +     * set of int values. +     * @param property The property being animated. Should not be null. +     * @param values The values that the property will animate between. +     * @return PropertyValuesHolder The constructed PropertyValuesHolder object. +     */ +    //public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) { +    //    return new IntPropertyValuesHolder(property, values); +    //} + +    /** +     * Constructs and returns a PropertyValuesHolder with a given property name and +     * set of float values. +     * @param propertyName The name of the property being animated. +     * @param values The values that the named property will animate between. +     * @return PropertyValuesHolder The constructed PropertyValuesHolder object. +     */ +    public static PropertyValuesHolder ofFloat(String propertyName, float... values) { +        return new FloatPropertyValuesHolder(propertyName, values); +    } + +    /** +     * Constructs and returns a PropertyValuesHolder with a given property and +     * set of float values. +     * @param property The property being animated. Should not be null. +     * @param values The values that the property will animate between. +     * @return PropertyValuesHolder The constructed PropertyValuesHolder object. +     */ +    //public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) { +    //    return new FloatPropertyValuesHolder(property, values); +    //} + +    /** +     * Constructs and returns a PropertyValuesHolder with a given property name and +     * set of Object values. This variant also takes a TypeEvaluator because the system +     * cannot automatically interpolate between objects of unknown type. +     * +     * @param propertyName The name of the property being animated. +     * @param evaluator A TypeEvaluator that will be called on each animation frame to +     * provide the necessary interpolation between the Object values to derive the animated +     * value. +     * @param values The values that the named property will animate between. +     * @return PropertyValuesHolder The constructed PropertyValuesHolder object. +     */ +    public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator, +            Object... values) { +        PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); +        pvh.setObjectValues(values); +        pvh.setEvaluator(evaluator); +        return pvh; +    } + +    /** +     * Constructs and returns a PropertyValuesHolder with a given property and +     * set of Object values. This variant also takes a TypeEvaluator because the system +     * cannot automatically interpolate between objects of unknown type. +     * +     * @param property The property being animated. Should not be null. +     * @param evaluator A TypeEvaluator that will be called on each animation frame to +     * provide the necessary interpolation between the Object values to derive the animated +     * value. +     * @param values The values that the property will animate between. +     * @return PropertyValuesHolder The constructed PropertyValuesHolder object. +     */ +    //public static <V> PropertyValuesHolder ofObject(Property property, +    //        TypeEvaluator<V> evaluator, V... values) { +    //    PropertyValuesHolder pvh = new PropertyValuesHolder(property); +    //    pvh.setObjectValues(values); +    //    pvh.setEvaluator(evaluator); +    //    return pvh; +    //} + +    /** +     * Constructs and returns a PropertyValuesHolder object with the specified property name and set +     * of values. These values can be of any type, but the type should be consistent so that +     * an appropriate {@link android.animation.TypeEvaluator} can be found that matches +     * the common type. +     * <p>If there is only one value, it is assumed to be the end value of an animation, +     * and an initial value will be derived, if possible, by calling a getter function +     * on the object. Also, if any value is null, the value will be filled in when the animation +     * starts in the same way. This mechanism of automatically getting null values only works +     * if the PropertyValuesHolder object is used in conjunction +     * {@link ObjectAnimator}, and with a getter function +     * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has +     * no way of determining what the value should be. +     * @param propertyName The name of the property associated with this set of values. This +     * can be the actual property name to be used when using a ObjectAnimator object, or +     * just a name used to get animated values, such as if this object is used with an +     * ValueAnimator object. +     * @param values The set of values to animate between. +     */ +    public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) { +        KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); +        if (keyframeSet instanceof IntKeyframeSet) { +            return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet); +        } else if (keyframeSet instanceof FloatKeyframeSet) { +            return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet); +        } +        else { +            PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); +            pvh.mKeyframeSet = keyframeSet; +            pvh.mValueType = values[0].getType(); +            return pvh; +        } +    } + +    /** +     * Constructs and returns a PropertyValuesHolder object with the specified property and set +     * of values. These values can be of any type, but the type should be consistent so that +     * an appropriate {@link android.animation.TypeEvaluator} can be found that matches +     * the common type. +     * <p>If there is only one value, it is assumed to be the end value of an animation, +     * and an initial value will be derived, if possible, by calling the property's +     * {@link android.util.Property#get(Object)} function. +     * Also, if any value is null, the value will be filled in when the animation +     * starts in the same way. This mechanism of automatically getting null values only works +     * if the PropertyValuesHolder object is used in conjunction with +     * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has +     * no way of determining what the value should be. +     * @param property The property associated with this set of values. Should not be null. +     * @param values The set of values to animate between. +     */ +    //public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) { +    //    KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); +    //    if (keyframeSet instanceof IntKeyframeSet) { +    //        return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet); +    //    } else if (keyframeSet instanceof FloatKeyframeSet) { +    //        return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet); +    //    } +    //    else { +    //        PropertyValuesHolder pvh = new PropertyValuesHolder(property); +    //        pvh.mKeyframeSet = keyframeSet; +    //        pvh.mValueType = ((Keyframe)values[0]).getType(); +    //        return pvh; +    //    } +    //} + +    /** +     * Set the animated values for this object to this set of ints. +     * If there is only one value, it is assumed to be the end value of an animation, +     * and an initial value will be derived, if possible, by calling a getter function +     * on the object. Also, if any value is null, the value will be filled in when the animation +     * starts in the same way. This mechanism of automatically getting null values only works +     * if the PropertyValuesHolder object is used in conjunction +     * {@link ObjectAnimator}, and with a getter function +     * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has +     * no way of determining what the value should be. +     * +     * @param values One or more values that the animation will animate between. +     */ +    public void setIntValues(int... values) { +        mValueType = int.class; +        mKeyframeSet = KeyframeSet.ofInt(values); +    } + +    /** +     * Set the animated values for this object to this set of floats. +     * If there is only one value, it is assumed to be the end value of an animation, +     * and an initial value will be derived, if possible, by calling a getter function +     * on the object. Also, if any value is null, the value will be filled in when the animation +     * starts in the same way. This mechanism of automatically getting null values only works +     * if the PropertyValuesHolder object is used in conjunction +     * {@link ObjectAnimator}, and with a getter function +     * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has +     * no way of determining what the value should be. +     * +     * @param values One or more values that the animation will animate between. +     */ +    public void setFloatValues(float... values) { +        mValueType = float.class; +        mKeyframeSet = KeyframeSet.ofFloat(values); +    } + +    /** +     * Set the animated values for this object to this set of Keyframes. +     * +     * @param values One or more values that the animation will animate between. +     */ +    public void setKeyframes(Keyframe... values) { +        int numKeyframes = values.length; +        Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)]; +        mValueType = values[0].getType(); +        for (int i = 0; i < numKeyframes; ++i) { +            keyframes[i] = values[i]; +        } +        mKeyframeSet = new KeyframeSet(keyframes); +    } + +    /** +     * Set the animated values for this object to this set of Objects. +     * If there is only one value, it is assumed to be the end value of an animation, +     * and an initial value will be derived, if possible, by calling a getter function +     * on the object. Also, if any value is null, the value will be filled in when the animation +     * starts in the same way. This mechanism of automatically getting null values only works +     * if the PropertyValuesHolder object is used in conjunction +     * {@link ObjectAnimator}, and with a getter function +     * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has +     * no way of determining what the value should be. +     * +     * @param values One or more values that the animation will animate between. +     */ +    public void setObjectValues(Object... values) { +        mValueType = values[0].getClass(); +        mKeyframeSet = KeyframeSet.ofObject(values); +    } + +    /** +     * Determine the setter or getter function using the JavaBeans convention of setFoo or +     * getFoo for a property named 'foo'. This function figures out what the name of the +     * function should be and uses reflection to find the Method with that name on the +     * target object. +     * +     * @param targetClass The class to search for the method +     * @param prefix "set" or "get", depending on whether we need a setter or getter. +     * @param valueType The type of the parameter (in the case of a setter). This type +     * is derived from the values set on this PropertyValuesHolder. This type is used as +     * a first guess at the parameter type, but we check for methods with several different +     * types to avoid problems with slight mis-matches between supplied values and actual +     * value types used on the setter. +     * @return Method the method associated with mPropertyName. +     */ +    private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { +        // TODO: faster implementation... +        Method returnVal = null; +        String methodName = getMethodName(prefix, mPropertyName); +        Class args[] = null; +        if (valueType == null) { +            try { +                returnVal = targetClass.getMethod(methodName, args); +            } catch (NoSuchMethodException e) { +                Log.e("PropertyValuesHolder", targetClass.getSimpleName() + " - " + +                        "Couldn't find no-arg method for property " + mPropertyName + ": " + e); +            } +        } else { +            args = new Class[1]; +            Class typeVariants[]; +            if (mValueType.equals(Float.class)) { +                typeVariants = FLOAT_VARIANTS; +            } else if (mValueType.equals(Integer.class)) { +                typeVariants = INTEGER_VARIANTS; +            } else if (mValueType.equals(Double.class)) { +                typeVariants = DOUBLE_VARIANTS; +            } else { +                typeVariants = new Class[1]; +                typeVariants[0] = mValueType; +            } +            for (Class typeVariant : typeVariants) { +                args[0] = typeVariant; +                try { +                    returnVal = targetClass.getMethod(methodName, args); +                    // change the value type to suit +                    mValueType = typeVariant; +                    return returnVal; +                } catch (NoSuchMethodException e) { +                    // Swallow the error and keep trying other variants +                } +            } +            // If we got here, then no appropriate function was found +            Log.e("PropertyValuesHolder", +                    "Couldn't find " + prefix + "ter property " + mPropertyName + +                            " for " + targetClass.getSimpleName() + +                            " with value type "+ mValueType); +        } + +        return returnVal; +    } + + +    /** +     * Returns the setter or getter requested. This utility function checks whether the +     * requested method exists in the propertyMapMap cache. If not, it calls another +     * utility function to request the Method from the targetClass directly. +     * @param targetClass The Class on which the requested method should exist. +     * @param propertyMapMap The cache of setters/getters derived so far. +     * @param prefix "set" or "get", for the setter or getter. +     * @param valueType The type of parameter passed into the method (null for getter). +     * @return Method the method associated with mPropertyName. +     */ +    private Method setupSetterOrGetter(Class targetClass, +            HashMap<Class, HashMap<String, Method>> propertyMapMap, +            String prefix, Class valueType) { +        Method setterOrGetter = null; +        try { +            // Have to lock property map prior to reading it, to guard against +            // another thread putting something in there after we've checked it +            // but before we've added an entry to it +            mPropertyMapLock.writeLock().lock(); +            HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); +            if (propertyMap != null) { +                setterOrGetter = propertyMap.get(mPropertyName); +            } +            if (setterOrGetter == null) { +                setterOrGetter = getPropertyFunction(targetClass, prefix, valueType); +                if (propertyMap == null) { +                    propertyMap = new HashMap<String, Method>(); +                    propertyMapMap.put(targetClass, propertyMap); +                } +                propertyMap.put(mPropertyName, setterOrGetter); +            } +        } finally { +            mPropertyMapLock.writeLock().unlock(); +        } +        return setterOrGetter; +    } + +    /** +     * Utility function to get the setter from targetClass +     * @param targetClass The Class on which the requested method should exist. +     */ +    void setupSetter(Class targetClass) { +        mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType); +    } + +    /** +     * Utility function to get the getter from targetClass +     */ +    private void setupGetter(Class targetClass) { +        mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null); +    } + +    /** +     * Internal function (called from ObjectAnimator) to set up the setter and getter +     * prior to running the animation. If the setter has not been manually set for this +     * object, it will be derived automatically given the property name, target object, and +     * types of values supplied. If no getter has been set, it will be supplied iff any of the +     * supplied values was null. If there is a null value, then the getter (supplied or derived) +     * will be called to set those null values to the current value of the property +     * on the target object. +     * @param target The object on which the setter (and possibly getter) exist. +     */ +    void setupSetterAndGetter(Object target) { +        //if (mProperty != null) { +        //    // check to make sure that mProperty is on the class of target +        //    try { +        //        Object testValue = mProperty.get(target); +        //        for (Keyframe kf : mKeyframeSet.mKeyframes) { +        //            if (!kf.hasValue()) { +        //                kf.setValue(mProperty.get(target)); +        //            } +        //        } +        //        return; +        //    } catch (ClassCastException e) { +        //        Log.e("PropertyValuesHolder","No such property (" + mProperty.getName() + +        //                ") on target object " + target + ". Trying reflection instead"); +        //        mProperty = null; +        //    } +        //} +        Class targetClass = target.getClass(); +        if (mSetter == null) { +            setupSetter(targetClass); +        } +        for (Keyframe kf : mKeyframeSet.mKeyframes) { +            if (!kf.hasValue()) { +                if (mGetter == null) { +                    setupGetter(targetClass); +                } +                try { +                    kf.setValue(mGetter.invoke(target)); +                } catch (InvocationTargetException e) { +                    Log.e("PropertyValuesHolder", e.toString()); +                } catch (IllegalAccessException e) { +                    Log.e("PropertyValuesHolder", e.toString()); +                } +            } +        } +    } + +    /** +     * Utility function to set the value stored in a particular Keyframe. The value used is +     * whatever the value is for the property name specified in the keyframe on the target object. +     * +     * @param target The target object from which the current value should be extracted. +     * @param kf The keyframe which holds the property name and value. +     */ +    private void setupValue(Object target, Keyframe kf) { +        //if (mProperty != null) { +        //    kf.setValue(mProperty.get(target)); +        //} +        try { +            if (mGetter == null) { +                Class targetClass = target.getClass(); +                setupGetter(targetClass); +            } +            kf.setValue(mGetter.invoke(target)); +        } catch (InvocationTargetException e) { +            Log.e("PropertyValuesHolder", e.toString()); +        } catch (IllegalAccessException e) { +            Log.e("PropertyValuesHolder", e.toString()); +        } +    } + +    /** +     * This function is called by ObjectAnimator when setting the start values for an animation. +     * The start values are set according to the current values in the target object. The +     * property whose value is extracted is whatever is specified by the propertyName of this +     * PropertyValuesHolder object. +     * +     * @param target The object which holds the start values that should be set. +     */ +    void setupStartValue(Object target) { +        setupValue(target, mKeyframeSet.mKeyframes.get(0)); +    } + +    /** +     * This function is called by ObjectAnimator when setting the end values for an animation. +     * The end values are set according to the current values in the target object. The +     * property whose value is extracted is whatever is specified by the propertyName of this +     * PropertyValuesHolder object. +     * +     * @param target The object which holds the start values that should be set. +     */ +    void setupEndValue(Object target) { +        setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1)); +    } + +    @Override +    public PropertyValuesHolder clone() { +        try { +            PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone(); +            newPVH.mPropertyName = mPropertyName; +            //newPVH.mProperty = mProperty; +            newPVH.mKeyframeSet = mKeyframeSet.clone(); +            newPVH.mEvaluator = mEvaluator; +            return newPVH; +        } catch (CloneNotSupportedException e) { +            // won't reach here +            return null; +        } +    } + +    /** +     * Internal function to set the value on the target object, using the setter set up +     * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator +     * to handle turning the value calculated by ValueAnimator into a value set on the object +     * according to the name of the property. +     * @param target The target object on which the value is set +     */ +    void setAnimatedValue(Object target) { +        //if (mProperty != null) { +        //    mProperty.set(target, getAnimatedValue()); +        //} +        if (mSetter != null) { +            try { +                mTmpValueArray[0] = getAnimatedValue(); +                mSetter.invoke(target, mTmpValueArray); +            } catch (InvocationTargetException e) { +                Log.e("PropertyValuesHolder", e.toString()); +            } catch (IllegalAccessException e) { +                Log.e("PropertyValuesHolder", e.toString()); +            } +        } +    } + +    /** +     * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used +     * to calculate animated values. +     */ +    void init() { +        if (mEvaluator == null) { +            // We already handle int and float automatically, but not their Object +            // equivalents +            mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : +                    (mValueType == Float.class) ? sFloatEvaluator : +                    null; +        } +        if (mEvaluator != null) { +            // KeyframeSet knows how to evaluate the common types - only give it a custom +            // evaluator if one has been set on this class +            mKeyframeSet.setEvaluator(mEvaluator); +        } +    } + +    /** +     * The TypeEvaluator will the automatically determined based on the type of values +     * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so +     * desired. This may be important in cases where either the type of the values supplied +     * do not match the way that they should be interpolated between, or if the values +     * are of a custom type or one not currently understood by the animation system. Currently, +     * only values of type float and int (and their Object equivalents: Float +     * and Integer) are  correctly interpolated; all other types require setting a TypeEvaluator. +     * @param evaluator +     */ +    public void setEvaluator(TypeEvaluator evaluator) { +        mEvaluator = evaluator; +        mKeyframeSet.setEvaluator(evaluator); +    } + +    /** +     * Function used to calculate the value according to the evaluator set up for +     * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue(). +     * +     * @param fraction The elapsed, interpolated fraction of the animation. +     */ +    void calculateValue(float fraction) { +        mAnimatedValue = mKeyframeSet.getValue(fraction); +    } + +    /** +     * Sets the name of the property that will be animated. This name is used to derive +     * a setter function that will be called to set animated values. +     * For example, a property name of <code>foo</code> will result +     * in a call to the function <code>setFoo()</code> on the target object. If either +     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will +     * also be derived and called. +     * +     * <p>Note that the setter function derived from this property name +     * must take the same parameter type as the +     * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to +     * the setter function will fail.</p> +     * +     * @param propertyName The name of the property being animated. +     */ +    public void setPropertyName(String propertyName) { +        mPropertyName = propertyName; +    } + +    /** +     * Sets the property that will be animated. +     * +     * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property +     * must exist on the target object specified in that ObjectAnimator.</p> +     * +     * @param property The property being animated. +     */ +    //public void setProperty(Property property) { +    //    mProperty = property; +    //} + +    /** +     * Gets the name of the property that will be animated. This name will be used to derive +     * a setter function that will be called to set animated values. +     * For example, a property name of <code>foo</code> will result +     * in a call to the function <code>setFoo()</code> on the target object. If either +     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will +     * also be derived and called. +     */ +    public String getPropertyName() { +        return mPropertyName; +    } + +    /** +     * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value +     * most recently calculated in calculateValue(). +     * @return +     */ +    Object getAnimatedValue() { +        return mAnimatedValue; +    } + +    @Override +    public String toString() { +        return mPropertyName + ": " + mKeyframeSet.toString(); +    } + +    /** +     * Utility method to derive a setter/getter method name from a property name, where the +     * prefix is typically "set" or "get" and the first letter of the property name is +     * capitalized. +     * +     * @param prefix The precursor to the method name, before the property name begins, typically +     * "set" or "get". +     * @param propertyName The name of the property that represents the bulk of the method name +     * after the prefix. The first letter of this word will be capitalized in the resulting +     * method name. +     * @return String the property name converted to a method name according to the conventions +     * specified above. +     */ +    static String getMethodName(String prefix, String propertyName) { +        if (propertyName == null || propertyName.length() == 0) { +            // shouldn't get here +            return prefix; +        } +        char firstLetter = Character.toUpperCase(propertyName.charAt(0)); +        String theRest = propertyName.substring(1); +        return prefix + firstLetter + theRest; +    } + +    static class IntPropertyValuesHolder extends PropertyValuesHolder { + +        // Cache JNI functions to avoid looking them up twice +        //private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = +        //        new HashMap<Class, HashMap<String, Integer>>(); +        //int mJniSetter; +        //private IntProperty mIntProperty; + +        IntKeyframeSet mIntKeyframeSet; +        int mIntAnimatedValue; + +        public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) { +            super(propertyName); +            mValueType = int.class; +            mKeyframeSet = keyframeSet; +            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; +        } + +        //public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) { +        //    super(property); +        //    mValueType = int.class; +        //    mKeyframeSet = keyframeSet; +        //    mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; +        //    if (property instanceof  IntProperty) { +        //        mIntProperty = (IntProperty) mProperty; +        //    } +        //} + +        public IntPropertyValuesHolder(String propertyName, int... values) { +            super(propertyName); +            setIntValues(values); +        } + +        //public IntPropertyValuesHolder(Property property, int... values) { +        //    super(property); +        //    setIntValues(values); +        //    if (property instanceof  IntProperty) { +        //        mIntProperty = (IntProperty) mProperty; +        //    } +        //} + +        @Override +        public void setIntValues(int... values) { +            super.setIntValues(values); +            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; +        } + +        @Override +        void calculateValue(float fraction) { +            mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction); +        } + +        @Override +        Object getAnimatedValue() { +            return mIntAnimatedValue; +        } + +        @Override +        public IntPropertyValuesHolder clone() { +            IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone(); +            newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet; +            return newPVH; +        } + +        /** +         * Internal function to set the value on the target object, using the setter set up +         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator +         * to handle turning the value calculated by ValueAnimator into a value set on the object +         * according to the name of the property. +         * @param target The target object on which the value is set +         */ +        @Override +        void setAnimatedValue(Object target) { +            //if (mIntProperty != null) { +            //    mIntProperty.setValue(target, mIntAnimatedValue); +            //    return; +            //} +            //if (mProperty != null) { +            //    mProperty.set(target, mIntAnimatedValue); +            //    return; +            //} +            //if (mJniSetter != 0) { +            //    nCallIntMethod(target, mJniSetter, mIntAnimatedValue); +            //    return; +            //} +            if (mSetter != null) { +                try { +                    mTmpValueArray[0] = mIntAnimatedValue; +                    mSetter.invoke(target, mTmpValueArray); +                } catch (InvocationTargetException e) { +                    Log.e("PropertyValuesHolder", e.toString()); +                } catch (IllegalAccessException e) { +                    Log.e("PropertyValuesHolder", e.toString()); +                } +            } +        } + +        @Override +        void setupSetter(Class targetClass) { +            //if (mProperty != null) { +            //    return; +            //} +            // Check new static hashmap<propName, int> for setter method +            //try { +            //    mPropertyMapLock.writeLock().lock(); +            //    HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); +            //    if (propertyMap != null) { +            //        Integer mJniSetterInteger = propertyMap.get(mPropertyName); +            //        if (mJniSetterInteger != null) { +            //            mJniSetter = mJniSetterInteger; +            //        } +            //    } +            //    if (mJniSetter == 0) { +            //        String methodName = getMethodName("set", mPropertyName); +            //        mJniSetter = nGetIntMethod(targetClass, methodName); +            //        if (mJniSetter != 0) { +            //            if (propertyMap == null) { +            //                propertyMap = new HashMap<String, Integer>(); +            //                sJNISetterPropertyMap.put(targetClass, propertyMap); +            //            } +            //            propertyMap.put(mPropertyName, mJniSetter); +            //        } +            //    } +            //} catch (NoSuchMethodError e) { +            //    Log.d("PropertyValuesHolder", +            //            "Can't find native method using JNI, use reflection" + e); +            //} finally { +            //    mPropertyMapLock.writeLock().unlock(); +            //} +            //if (mJniSetter == 0) { +                // Couldn't find method through fast JNI approach - just use reflection +                super.setupSetter(targetClass); +            //} +        } +    } + +    static class FloatPropertyValuesHolder extends PropertyValuesHolder { + +        // Cache JNI functions to avoid looking them up twice +        //private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = +        //        new HashMap<Class, HashMap<String, Integer>>(); +        //int mJniSetter; +        //private FloatProperty mFloatProperty; + +        FloatKeyframeSet mFloatKeyframeSet; +        float mFloatAnimatedValue; + +        public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) { +            super(propertyName); +            mValueType = float.class; +            mKeyframeSet = keyframeSet; +            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; +        } + +        //public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) { +        //    super(property); +        //    mValueType = float.class; +        //    mKeyframeSet = keyframeSet; +        //    mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; +        //    if (property instanceof FloatProperty) { +        //        mFloatProperty = (FloatProperty) mProperty; +        //    } +        //} + +        public FloatPropertyValuesHolder(String propertyName, float... values) { +            super(propertyName); +            setFloatValues(values); +        } + +        //public FloatPropertyValuesHolder(Property property, float... values) { +        //    super(property); +        //    setFloatValues(values); +        //    if (property instanceof  FloatProperty) { +        //        mFloatProperty = (FloatProperty) mProperty; +        //    } +        //} + +        @Override +        public void setFloatValues(float... values) { +            super.setFloatValues(values); +            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; +        } + +        @Override +        void calculateValue(float fraction) { +            mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction); +        } + +        @Override +        Object getAnimatedValue() { +            return mFloatAnimatedValue; +        } + +        @Override +        public FloatPropertyValuesHolder clone() { +            FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone(); +            newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet; +            return newPVH; +        } + +        /** +         * Internal function to set the value on the target object, using the setter set up +         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator +         * to handle turning the value calculated by ValueAnimator into a value set on the object +         * according to the name of the property. +         * @param target The target object on which the value is set +         */ +        @Override +        void setAnimatedValue(Object target) { +            //if (mFloatProperty != null) { +            //    mFloatProperty.setValue(target, mFloatAnimatedValue); +            //    return; +            //} +            //if (mProperty != null) { +            //    mProperty.set(target, mFloatAnimatedValue); +            //    return; +            //} +            //if (mJniSetter != 0) { +            //    nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue); +            //    return; +            //} +            if (mSetter != null) { +                try { +                    mTmpValueArray[0] = mFloatAnimatedValue; +                    mSetter.invoke(target, mTmpValueArray); +                } catch (InvocationTargetException e) { +                    Log.e("PropertyValuesHolder", e.toString()); +                } catch (IllegalAccessException e) { +                    Log.e("PropertyValuesHolder", e.toString()); +                } +            } +        } + +        @Override +        void setupSetter(Class targetClass) { +            //if (mProperty != null) { +            //    return; +            //} +            // Check new static hashmap<propName, int> for setter method +            //try { +            //    mPropertyMapLock.writeLock().lock(); +            //    HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); +            //    if (propertyMap != null) { +            //        Integer mJniSetterInteger = propertyMap.get(mPropertyName); +            //        if (mJniSetterInteger != null) { +            //            mJniSetter = mJniSetterInteger; +            //        } +            //    } +            //    if (mJniSetter == 0) { +            //        String methodName = getMethodName("set", mPropertyName); +            //        mJniSetter = nGetFloatMethod(targetClass, methodName); +            //        if (mJniSetter != 0) { +            //            if (propertyMap == null) { +            //                propertyMap = new HashMap<String, Integer>(); +            //                sJNISetterPropertyMap.put(targetClass, propertyMap); +            //            } +            //            propertyMap.put(mPropertyName, mJniSetter); +            //        } +            //    } +            //} catch (NoSuchMethodError e) { +            //    Log.d("PropertyValuesHolder", +            //            "Can't find native method using JNI, use reflection" + e); +            //} finally { +            //    mPropertyMapLock.writeLock().unlock(); +            //} +            //if (mJniSetter == 0) { +                // Couldn't find method through fast JNI approach - just use reflection +                super.setupSetter(targetClass); +            //} +        } + +    } + +    //native static private int nGetIntMethod(Class targetClass, String methodName); +    //native static private int nGetFloatMethod(Class targetClass, String methodName); +    //native static private void nCallIntMethod(Object target, int methodID, int arg); +    //native static private void nCallFloatMethod(Object target, int methodID, float arg); +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java new file mode 100644 index 000000000..0ea319244 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +/** + * Interface for use with the {@link ValueAnimator#setEvaluator(TypeEvaluator)} function. Evaluators + * allow developers to create animations on arbitrary property types, by allowing them to supply + * custom evaulators for types that are not automatically understood and used by the animation + * system. + * + * @see ValueAnimator#setEvaluator(TypeEvaluator) + */ +public interface TypeEvaluator<T> { + +    /** +     * This function returns the result of linearly interpolating the start and end values, with +     * <code>fraction</code> representing the proportion between the start and end values. The +     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>, +     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>, +     * and <code>t</code> is <code>fraction</code>. +     * +     * @param fraction   The fraction from the starting to the ending values +     * @param startValue The start value. +     * @param endValue   The end value. +     * @return A linear interpolation between the start and end values, given the +     *         <code>fraction</code> parameter. +     */ +    public T evaluate(float fraction, T startValue, T endValue); + +}
\ No newline at end of file diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java new file mode 100644 index 000000000..d8a12c688 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java @@ -0,0 +1,1265 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.AndroidRuntimeException; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * This class provides a simple timing engine for running animations + * which calculate animated values and set them on target objects. + * + * <p>There is a single timing pulse that all animations use. It runs in a + * custom handler to ensure that property changes happen on the UI thread.</p> + * + * <p>By default, ValueAnimator uses non-linear time interpolation, via the + * {@link AccelerateDecelerateInterpolator} class, which accelerates into and decelerates + * out of an animation. This behavior can be changed by calling + * {@link ValueAnimator#setInterpolator(TimeInterpolator)}.</p> + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +public class ValueAnimator extends Animator { + +    /** +     * Internal constants +     */ + +    /* +     * The default amount of time in ms between animation frames +     */ +    private static final long DEFAULT_FRAME_DELAY = 10; + +    /** +     * Messages sent to timing handler: START is sent when an animation first begins, FRAME is sent +     * by the handler to itself to process the next animation frame +     */ +    static final int ANIMATION_START = 0; +    static final int ANIMATION_FRAME = 1; + +    /** +     * Values used with internal variable mPlayingState to indicate the current state of an +     * animation. +     */ +    static final int STOPPED    = 0; // Not yet playing +    static final int RUNNING    = 1; // Playing normally +    static final int SEEKED     = 2; // Seeked to some time value + +    /** +     * Internal variables +     * NOTE: This object implements the clone() method, making a deep copy of any referenced +     * objects. As other non-trivial fields are added to this class, make sure to add logic +     * to clone() to make deep copies of them. +     */ + +    // The first time that the animation's animateFrame() method is called. This time is used to +    // determine elapsed time (and therefore the elapsed fraction) in subsequent calls +    // to animateFrame() +    long mStartTime; + +    /** +     * Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked +     * to a value. +     */ +    long mSeekTime = -1; + +    // TODO: We access the following ThreadLocal variables often, some of them on every update. +    // If ThreadLocal access is significantly expensive, we may want to put all of these +    // fields into a structure sot hat we just access ThreadLocal once to get the reference +    // to that structure, then access the structure directly for each field. + +    // The static sAnimationHandler processes the internal timing loop on which all animations +    // are based +    private static ThreadLocal<AnimationHandler> sAnimationHandler = +            new ThreadLocal<AnimationHandler>(); + +    // The per-thread list of all active animations +    private static final ThreadLocal<ArrayList<ValueAnimator>> sAnimations = +            new ThreadLocal<ArrayList<ValueAnimator>>() { +                @Override +                protected ArrayList<ValueAnimator> initialValue() { +                    return new ArrayList<ValueAnimator>(); +                } +            }; + +    // The per-thread set of animations to be started on the next animation frame +    private static final ThreadLocal<ArrayList<ValueAnimator>> sPendingAnimations = +            new ThreadLocal<ArrayList<ValueAnimator>>() { +                @Override +                protected ArrayList<ValueAnimator> initialValue() { +                    return new ArrayList<ValueAnimator>(); +                } +            }; + +    /** +     * Internal per-thread collections used to avoid set collisions as animations start and end +     * while being processed. +     */ +    private static final ThreadLocal<ArrayList<ValueAnimator>> sDelayedAnims = +            new ThreadLocal<ArrayList<ValueAnimator>>() { +                @Override +                protected ArrayList<ValueAnimator> initialValue() { +                    return new ArrayList<ValueAnimator>(); +                } +            }; + +    private static final ThreadLocal<ArrayList<ValueAnimator>> sEndingAnims = +            new ThreadLocal<ArrayList<ValueAnimator>>() { +                @Override +                protected ArrayList<ValueAnimator> initialValue() { +                    return new ArrayList<ValueAnimator>(); +                } +            }; + +    private static final ThreadLocal<ArrayList<ValueAnimator>> sReadyAnims = +            new ThreadLocal<ArrayList<ValueAnimator>>() { +                @Override +                protected ArrayList<ValueAnimator> initialValue() { +                    return new ArrayList<ValueAnimator>(); +                } +            }; + +    // The time interpolator to be used if none is set on the animation +    private static final /*Time*/Interpolator sDefaultInterpolator = +            new AccelerateDecelerateInterpolator(); + +    // type evaluators for the primitive types handled by this implementation +    //private static final TypeEvaluator sIntEvaluator = new IntEvaluator(); +    //private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator(); + +    /** +     * Used to indicate whether the animation is currently playing in reverse. This causes the +     * elapsed fraction to be inverted to calculate the appropriate values. +     */ +    private boolean mPlayingBackwards = false; + +    /** +     * This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the +     * repeatCount (if repeatCount!=INFINITE), the animation ends +     */ +    private int mCurrentIteration = 0; + +    /** +     * Tracks current elapsed/eased fraction, for querying in getAnimatedFraction(). +     */ +    private float mCurrentFraction = 0f; + +    /** +     * Tracks whether a startDelay'd animation has begun playing through the startDelay. +     */ +    private boolean mStartedDelay = false; + +    /** +     * Tracks the time at which the animation began playing through its startDelay. This is +     * different from the mStartTime variable, which is used to track when the animation became +     * active (which is when the startDelay expired and the animation was added to the active +     * animations list). +     */ +    private long mDelayStartTime; + +    /** +     * Flag that represents the current state of the animation. Used to figure out when to start +     * an animation (if state == STOPPED). Also used to end an animation that +     * has been cancel()'d or end()'d since the last animation frame. Possible values are +     * STOPPED, RUNNING, SEEKED. +     */ +    int mPlayingState = STOPPED; + +    /** +     * Additional playing state to indicate whether an animator has been start()'d. There is +     * some lag between a call to start() and the first animation frame. We should still note +     * that the animation has been started, even if it's first animation frame has not yet +     * happened, and reflect that state in isRunning(). +     * Note that delayed animations are different: they are not started until their first +     * animation frame, which occurs after their delay elapses. +     */ +    private boolean mRunning = false; + +    /** +     * Additional playing state to indicate whether an animator has been start()'d, whether or +     * not there is a nonzero startDelay. +     */ +    private boolean mStarted = false; + +    /** +     * Flag that denotes whether the animation is set up and ready to go. Used to +     * set up animation that has not yet been started. +     */ +    boolean mInitialized = false; + +    // +    // Backing variables +    // + +    // How long the animation should last in ms +    private long mDuration = 300; + +    // The amount of time in ms to delay starting the animation after start() is called +    private long mStartDelay = 0; + +    // The number of milliseconds between animation frames +    private static long sFrameDelay = DEFAULT_FRAME_DELAY; + +    // The number of times the animation will repeat. The default is 0, which means the animation +    // will play only once +    private int mRepeatCount = 0; + +    /** +     * The type of repetition that will occur when repeatMode is nonzero. RESTART means the +     * animation will start from the beginning on every new cycle. REVERSE means the animation +     * will reverse directions on each iteration. +     */ +    private int mRepeatMode = RESTART; + +    /** +     * The time interpolator to be used. The elapsed fraction of the animation will be passed +     * through this interpolator to calculate the interpolated fraction, which is then used to +     * calculate the animated values. +     */ +    private /*Time*/Interpolator mInterpolator = sDefaultInterpolator; + +    /** +     * The set of listeners to be sent events through the life of an animation. +     */ +    private ArrayList<AnimatorUpdateListener> mUpdateListeners = null; + +    /** +     * The property/value sets being animated. +     */ +    PropertyValuesHolder[] mValues; + +    /** +     * A hashmap of the PropertyValuesHolder objects. This map is used to lookup animated values +     * by property name during calls to getAnimatedValue(String). +     */ +    HashMap<String, PropertyValuesHolder> mValuesMap; + +    /** +     * Public constants +     */ + +    /** +     * When the animation reaches the end and <code>repeatCount</code> is INFINITE +     * or a positive value, the animation restarts from the beginning. +     */ +    public static final int RESTART = 1; +    /** +     * When the animation reaches the end and <code>repeatCount</code> is INFINITE +     * or a positive value, the animation reverses direction on every iteration. +     */ +    public static final int REVERSE = 2; +    /** +     * This value used used with the {@link #setRepeatCount(int)} property to repeat +     * the animation indefinitely. +     */ +    public static final int INFINITE = -1; + +    /** +     * Creates a new ValueAnimator object. This default constructor is primarily for +     * use internally; the factory methods which take parameters are more generally +     * useful. +     */ +    public ValueAnimator() { +    } + +    /** +     * Constructs and returns a ValueAnimator that animates between int values. A single +     * value implies that that value is the one being animated to. However, this is not typically +     * useful in a ValueAnimator object because there is no way for the object to determine the +     * starting value for the animation (unlike ObjectAnimator, which can derive that value +     * from the target object and property being animated). Therefore, there should typically +     * be two or more values. +     * +     * @param values A set of values that the animation will animate between over time. +     * @return A ValueAnimator object that is set up to animate between the given values. +     */ +    public static ValueAnimator ofInt(int... values) { +        ValueAnimator anim = new ValueAnimator(); +        anim.setIntValues(values); +        return anim; +    } + +    /** +     * Constructs and returns a ValueAnimator that animates between float values. A single +     * value implies that that value is the one being animated to. However, this is not typically +     * useful in a ValueAnimator object because there is no way for the object to determine the +     * starting value for the animation (unlike ObjectAnimator, which can derive that value +     * from the target object and property being animated). Therefore, there should typically +     * be two or more values. +     * +     * @param values A set of values that the animation will animate between over time. +     * @return A ValueAnimator object that is set up to animate between the given values. +     */ +    public static ValueAnimator ofFloat(float... values) { +        ValueAnimator anim = new ValueAnimator(); +        anim.setFloatValues(values); +        return anim; +    } + +    /** +     * Constructs and returns a ValueAnimator that animates between the values +     * specified in the PropertyValuesHolder objects. +     * +     * @param values A set of PropertyValuesHolder objects whose values will be animated +     * between over time. +     * @return A ValueAnimator object that is set up to animate between the given values. +     */ +    public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) { +        ValueAnimator anim = new ValueAnimator(); +        anim.setValues(values); +        return anim; +    } +    /** +     * Constructs and returns a ValueAnimator that animates between Object values. A single +     * value implies that that value is the one being animated to. However, this is not typically +     * useful in a ValueAnimator object because there is no way for the object to determine the +     * starting value for the animation (unlike ObjectAnimator, which can derive that value +     * from the target object and property being animated). Therefore, there should typically +     * be two or more values. +     * +     * <p>Since ValueAnimator does not know how to animate between arbitrary Objects, this +     * factory method also takes a TypeEvaluator object that the ValueAnimator will use +     * to perform that interpolation. +     * +     * @param evaluator A TypeEvaluator that will be called on each animation frame to +     * provide the ncessry interpolation between the Object values to derive the animated +     * value. +     * @param values A set of values that the animation will animate between over time. +     * @return A ValueAnimator object that is set up to animate between the given values. +     */ +    public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) { +        ValueAnimator anim = new ValueAnimator(); +        anim.setObjectValues(values); +        anim.setEvaluator(evaluator); +        return anim; +    } + +    /** +     * Sets int values that will be animated between. A single +     * value implies that that value is the one being animated to. However, this is not typically +     * useful in a ValueAnimator object because there is no way for the object to determine the +     * starting value for the animation (unlike ObjectAnimator, which can derive that value +     * from the target object and property being animated). Therefore, there should typically +     * be two or more values. +     * +     * <p>If there are already multiple sets of values defined for this ValueAnimator via more +     * than one PropertyValuesHolder object, this method will set the values for the first +     * of those objects.</p> +     * +     * @param values A set of values that the animation will animate between over time. +     */ +    public void setIntValues(int... values) { +        if (values == null || values.length == 0) { +            return; +        } +        if (mValues == null || mValues.length == 0) { +            setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofInt("", values)}); +        } else { +            PropertyValuesHolder valuesHolder = mValues[0]; +            valuesHolder.setIntValues(values); +        } +        // New property/values/target should cause re-initialization prior to starting +        mInitialized = false; +    } + +    /** +     * Sets float values that will be animated between. A single +     * value implies that that value is the one being animated to. However, this is not typically +     * useful in a ValueAnimator object because there is no way for the object to determine the +     * starting value for the animation (unlike ObjectAnimator, which can derive that value +     * from the target object and property being animated). Therefore, there should typically +     * be two or more values. +     * +     * <p>If there are already multiple sets of values defined for this ValueAnimator via more +     * than one PropertyValuesHolder object, this method will set the values for the first +     * of those objects.</p> +     * +     * @param values A set of values that the animation will animate between over time. +     */ +    public void setFloatValues(float... values) { +        if (values == null || values.length == 0) { +            return; +        } +        if (mValues == null || mValues.length == 0) { +            setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofFloat("", values)}); +        } else { +            PropertyValuesHolder valuesHolder = mValues[0]; +            valuesHolder.setFloatValues(values); +        } +        // New property/values/target should cause re-initialization prior to starting +        mInitialized = false; +    } + +    /** +     * Sets the values to animate between for this animation. A single +     * value implies that that value is the one being animated to. However, this is not typically +     * useful in a ValueAnimator object because there is no way for the object to determine the +     * starting value for the animation (unlike ObjectAnimator, which can derive that value +     * from the target object and property being animated). Therefore, there should typically +     * be two or more values. +     * +     * <p>If there are already multiple sets of values defined for this ValueAnimator via more +     * than one PropertyValuesHolder object, this method will set the values for the first +     * of those objects.</p> +     * +     * <p>There should be a TypeEvaluator set on the ValueAnimator that knows how to interpolate +     * between these value objects. ValueAnimator only knows how to interpolate between the +     * primitive types specified in the other setValues() methods.</p> +     * +     * @param values The set of values to animate between. +     */ +    public void setObjectValues(Object... values) { +        if (values == null || values.length == 0) { +            return; +        } +        if (mValues == null || mValues.length == 0) { +            setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofObject("", +                    (TypeEvaluator)null, values)}); +        } else { +            PropertyValuesHolder valuesHolder = mValues[0]; +            valuesHolder.setObjectValues(values); +        } +        // New property/values/target should cause re-initialization prior to starting +        mInitialized = false; +    } + +    /** +     * Sets the values, per property, being animated between. This function is called internally +     * by the constructors of ValueAnimator that take a list of values. But an ValueAnimator can +     * be constructed without values and this method can be called to set the values manually +     * instead. +     * +     * @param values The set of values, per property, being animated between. +     */ +    public void setValues(PropertyValuesHolder... values) { +        int numValues = values.length; +        mValues = values; +        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues); +        for (int i = 0; i < numValues; ++i) { +            PropertyValuesHolder valuesHolder = values[i]; +            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder); +        } +        // New property/values/target should cause re-initialization prior to starting +        mInitialized = false; +    } + +    /** +     * Returns the values that this ValueAnimator animates between. These values are stored in +     * PropertyValuesHolder objects, even if the ValueAnimator was created with a simple list +     * of value objects instead. +     * +     * @return PropertyValuesHolder[] An array of PropertyValuesHolder objects which hold the +     * values, per property, that define the animation. +     */ +    public PropertyValuesHolder[] getValues() { +        return mValues; +    } + +    /** +     * This function is called immediately before processing the first animation +     * frame of an animation. If there is a nonzero <code>startDelay</code>, the +     * function is called after that delay ends. +     * It takes care of the final initialization steps for the +     * animation. +     * +     *  <p>Overrides of this method should call the superclass method to ensure +     *  that internal mechanisms for the animation are set up correctly.</p> +     */ +    void initAnimation() { +        if (!mInitialized) { +            int numValues = mValues.length; +            for (int i = 0; i < numValues; ++i) { +                mValues[i].init(); +            } +            mInitialized = true; +        } +    } + + +    /** +     * Sets the length of the animation. The default duration is 300 milliseconds. +     * +     * @param duration The length of the animation, in milliseconds. This value cannot +     * be negative. +     * @return ValueAnimator The object called with setDuration(). This return +     * value makes it easier to compose statements together that construct and then set the +     * duration, as in <code>ValueAnimator.ofInt(0, 10).setDuration(500).start()</code>. +     */ +    public ValueAnimator setDuration(long duration) { +        if (duration < 0) { +            throw new IllegalArgumentException("Animators cannot have negative duration: " + +                    duration); +        } +        mDuration = duration; +        return this; +    } + +    /** +     * Gets the length of the animation. The default duration is 300 milliseconds. +     * +     * @return The length of the animation, in milliseconds. +     */ +    public long getDuration() { +        return mDuration; +    } + +    /** +     * Sets the position of the animation to the specified point in time. This time should +     * be between 0 and the total duration of the animation, including any repetition. If +     * the animation has not yet been started, then it will not advance forward after it is +     * set to this time; it will simply set the time to this value and perform any appropriate +     * actions based on that time. If the animation is already running, then setCurrentPlayTime() +     * will set the current playing time to this value and continue playing from that point. +     * +     * @param playTime The time, in milliseconds, to which the animation is advanced or rewound. +     */ +    public void setCurrentPlayTime(long playTime) { +        initAnimation(); +        long currentTime = AnimationUtils.currentAnimationTimeMillis(); +        if (mPlayingState != RUNNING) { +            mSeekTime = playTime; +            mPlayingState = SEEKED; +        } +        mStartTime = currentTime - playTime; +        animationFrame(currentTime); +    } + +    /** +     * Gets the current position of the animation in time, which is equal to the current +     * time minus the time that the animation started. An animation that is not yet started will +     * return a value of zero. +     * +     * @return The current position in time of the animation. +     */ +    public long getCurrentPlayTime() { +        if (!mInitialized || mPlayingState == STOPPED) { +            return 0; +        } +        return AnimationUtils.currentAnimationTimeMillis() - mStartTime; +    } + +    /** +     * This custom, static handler handles the timing pulse that is shared by +     * all active animations. This approach ensures that the setting of animation +     * values will happen on the UI thread and that all animations will share +     * the same times for calculating their values, which makes synchronizing +     * animations possible. +     * +     */ +    private static class AnimationHandler extends Handler { +        /** +         * There are only two messages that we care about: ANIMATION_START and +         * ANIMATION_FRAME. The START message is sent when an animation's start() +         * method is called. It cannot start synchronously when start() is called +         * because the call may be on the wrong thread, and it would also not be +         * synchronized with other animations because it would not start on a common +         * timing pulse. So each animation sends a START message to the handler, which +         * causes the handler to place the animation on the active animations queue and +         * start processing frames for that animation. +         * The FRAME message is the one that is sent over and over while there are any +         * active animations to process. +         */ +        @Override +        public void handleMessage(Message msg) { +            boolean callAgain = true; +            ArrayList<ValueAnimator> animations = sAnimations.get(); +            ArrayList<ValueAnimator> delayedAnims = sDelayedAnims.get(); +            switch (msg.what) { +                // TODO: should we avoid sending frame message when starting if we +                // were already running? +                case ANIMATION_START: +                    ArrayList<ValueAnimator> pendingAnimations = sPendingAnimations.get(); +                    if (animations.size() > 0 || delayedAnims.size() > 0) { +                        callAgain = false; +                    } +                    // pendingAnims holds any animations that have requested to be started +                    // We're going to clear sPendingAnimations, but starting animation may +                    // cause more to be added to the pending list (for example, if one animation +                    // starting triggers another starting). So we loop until sPendingAnimations +                    // is empty. +                    while (pendingAnimations.size() > 0) { +                        ArrayList<ValueAnimator> pendingCopy = +                                (ArrayList<ValueAnimator>) pendingAnimations.clone(); +                        pendingAnimations.clear(); +                        int count = pendingCopy.size(); +                        for (int i = 0; i < count; ++i) { +                            ValueAnimator anim = pendingCopy.get(i); +                            // If the animation has a startDelay, place it on the delayed list +                            if (anim.mStartDelay == 0) { +                                anim.startAnimation(); +                            } else { +                                delayedAnims.add(anim); +                            } +                        } +                    } +                    // fall through to process first frame of new animations +                case ANIMATION_FRAME: +                    // currentTime holds the common time for all animations processed +                    // during this frame +                    long currentTime = AnimationUtils.currentAnimationTimeMillis(); +                    ArrayList<ValueAnimator> readyAnims = sReadyAnims.get(); +                    ArrayList<ValueAnimator> endingAnims = sEndingAnims.get(); + +                    // First, process animations currently sitting on the delayed queue, adding +                    // them to the active animations if they are ready +                    int numDelayedAnims = delayedAnims.size(); +                    for (int i = 0; i < numDelayedAnims; ++i) { +                        ValueAnimator anim = delayedAnims.get(i); +                        if (anim.delayedAnimationFrame(currentTime)) { +                            readyAnims.add(anim); +                        } +                    } +                    int numReadyAnims = readyAnims.size(); +                    if (numReadyAnims > 0) { +                        for (int i = 0; i < numReadyAnims; ++i) { +                            ValueAnimator anim = readyAnims.get(i); +                            anim.startAnimation(); +                            anim.mRunning = true; +                            delayedAnims.remove(anim); +                        } +                        readyAnims.clear(); +                    } + +                    // Now process all active animations. The return value from animationFrame() +                    // tells the handler whether it should now be ended +                    int numAnims = animations.size(); +                    int i = 0; +                    while (i < numAnims) { +                        ValueAnimator anim = animations.get(i); +                        if (anim.animationFrame(currentTime)) { +                            endingAnims.add(anim); +                        } +                        if (animations.size() == numAnims) { +                            ++i; +                        } else { +                            // An animation might be canceled or ended by client code +                            // during the animation frame. Check to see if this happened by +                            // seeing whether the current index is the same as it was before +                            // calling animationFrame(). Another approach would be to copy +                            // animations to a temporary list and process that list instead, +                            // but that entails garbage and processing overhead that would +                            // be nice to avoid. +                            --numAnims; +                            endingAnims.remove(anim); +                        } +                    } +                    if (endingAnims.size() > 0) { +                        for (i = 0; i < endingAnims.size(); ++i) { +                            endingAnims.get(i).endAnimation(); +                        } +                        endingAnims.clear(); +                    } + +                    // If there are still active or delayed animations, call the handler again +                    // after the frameDelay +                    if (callAgain && (!animations.isEmpty() || !delayedAnims.isEmpty())) { +                        sendEmptyMessageDelayed(ANIMATION_FRAME, Math.max(0, sFrameDelay - +                            (AnimationUtils.currentAnimationTimeMillis() - currentTime))); +                    } +                    break; +            } +        } +    } + +    /** +     * The amount of time, in milliseconds, to delay starting the animation after +     * {@link #start()} is called. +     * +     * @return the number of milliseconds to delay running the animation +     */ +    public long getStartDelay() { +        return mStartDelay; +    } + +    /** +     * The amount of time, in milliseconds, to delay starting the animation after +     * {@link #start()} is called. + +     * @param startDelay The amount of the delay, in milliseconds +     */ +    public void setStartDelay(long startDelay) { +        this.mStartDelay = startDelay; +    } + +    /** +     * The amount of time, in milliseconds, between each frame of the animation. This is a +     * requested time that the animation will attempt to honor, but the actual delay between +     * frames may be different, depending on system load and capabilities. This is a static +     * function because the same delay will be applied to all animations, since they are all +     * run off of a single timing loop. +     * +     * @return the requested time between frames, in milliseconds +     */ +    public static long getFrameDelay() { +        return sFrameDelay; +    } + +    /** +     * The amount of time, in milliseconds, between each frame of the animation. This is a +     * requested time that the animation will attempt to honor, but the actual delay between +     * frames may be different, depending on system load and capabilities. This is a static +     * function because the same delay will be applied to all animations, since they are all +     * run off of a single timing loop. +     * +     * @param frameDelay the requested time between frames, in milliseconds +     */ +    public static void setFrameDelay(long frameDelay) { +        sFrameDelay = frameDelay; +    } + +    /** +     * The most recent value calculated by this <code>ValueAnimator</code> when there is just one +     * property being animated. This value is only sensible while the animation is running. The main +     * purpose for this read-only property is to retrieve the value from the <code>ValueAnimator</code> +     * during a call to {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which +     * is called during each animation frame, immediately after the value is calculated. +     * +     * @return animatedValue The value most recently calculated by this <code>ValueAnimator</code> for +     * the single property being animated. If there are several properties being animated +     * (specified by several PropertyValuesHolder objects in the constructor), this function +     * returns the animated value for the first of those objects. +     */ +    public Object getAnimatedValue() { +        if (mValues != null && mValues.length > 0) { +            return mValues[0].getAnimatedValue(); +        } +        // Shouldn't get here; should always have values unless ValueAnimator was set up wrong +        return null; +    } + +    /** +     * The most recent value calculated by this <code>ValueAnimator</code> for <code>propertyName</code>. +     * The main purpose for this read-only property is to retrieve the value from the +     * <code>ValueAnimator</code> during a call to +     * {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which +     * is called during each animation frame, immediately after the value is calculated. +     * +     * @return animatedValue The value most recently calculated for the named property +     * by this <code>ValueAnimator</code>. +     */ +    public Object getAnimatedValue(String propertyName) { +        PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName); +        if (valuesHolder != null) { +            return valuesHolder.getAnimatedValue(); +        } else { +            // At least avoid crashing if called with bogus propertyName +            return null; +        } +    } + +    /** +     * Sets how many times the animation should be repeated. If the repeat +     * count is 0, the animation is never repeated. If the repeat count is +     * greater than 0 or {@link #INFINITE}, the repeat mode will be taken +     * into account. The repeat count is 0 by default. +     * +     * @param value the number of times the animation should be repeated +     */ +    public void setRepeatCount(int value) { +        mRepeatCount = value; +    } +    /** +     * Defines how many times the animation should repeat. The default value +     * is 0. +     * +     * @return the number of times the animation should repeat, or {@link #INFINITE} +     */ +    public int getRepeatCount() { +        return mRepeatCount; +    } + +    /** +     * Defines what this animation should do when it reaches the end. This +     * setting is applied only when the repeat count is either greater than +     * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}. +     * +     * @param value {@link #RESTART} or {@link #REVERSE} +     */ +    public void setRepeatMode(int value) { +        mRepeatMode = value; +    } + +    /** +     * Defines what this animation should do when it reaches the end. +     * +     * @return either one of {@link #REVERSE} or {@link #RESTART} +     */ +    public int getRepeatMode() { +        return mRepeatMode; +    } + +    /** +     * Adds a listener to the set of listeners that are sent update events through the life of +     * an animation. This method is called on all listeners for every frame of the animation, +     * after the values for the animation have been calculated. +     * +     * @param listener the listener to be added to the current set of listeners for this animation. +     */ +    public void addUpdateListener(AnimatorUpdateListener listener) { +        if (mUpdateListeners == null) { +            mUpdateListeners = new ArrayList<AnimatorUpdateListener>(); +        } +        mUpdateListeners.add(listener); +    } + +    /** +     * Removes all listeners from the set listening to frame updates for this animation. +     */ +    public void removeAllUpdateListeners() { +        if (mUpdateListeners == null) { +            return; +        } +        mUpdateListeners.clear(); +        mUpdateListeners = null; +    } + +    /** +     * Removes a listener from the set listening to frame updates for this animation. +     * +     * @param listener the listener to be removed from the current set of update listeners +     * for this animation. +     */ +    public void removeUpdateListener(AnimatorUpdateListener listener) { +        if (mUpdateListeners == null) { +            return; +        } +        mUpdateListeners.remove(listener); +        if (mUpdateListeners.size() == 0) { +            mUpdateListeners = null; +        } +    } + + +    /** +     * The time interpolator used in calculating the elapsed fraction of this animation. The +     * interpolator determines whether the animation runs with linear or non-linear motion, +     * such as acceleration and deceleration. The default value is +     * {@link android.view.animation.AccelerateDecelerateInterpolator} +     * +     * @param value the interpolator to be used by this animation. A value of <code>null</code> +     * will result in linear interpolation. +     */ +    @Override +    public void setInterpolator(/*Time*/Interpolator value) { +        if (value != null) { +            mInterpolator = value; +        } else { +            mInterpolator = new LinearInterpolator(); +        } +    } + +    /** +     * Returns the timing interpolator that this ValueAnimator uses. +     * +     * @return The timing interpolator for this ValueAnimator. +     */ +    public /*Time*/Interpolator getInterpolator() { +        return mInterpolator; +    } + +    /** +     * The type evaluator to be used when calculating the animated values of this animation. +     * The system will automatically assign a float or int evaluator based on the type +     * of <code>startValue</code> and <code>endValue</code> in the constructor. But if these values +     * are not one of these primitive types, or if different evaluation is desired (such as is +     * necessary with int values that represent colors), a custom evaluator needs to be assigned. +     * For example, when running an animation on color values, the {@link ArgbEvaluator} +     * should be used to get correct RGB color interpolation. +     * +     * <p>If this ValueAnimator has only one set of values being animated between, this evaluator +     * will be used for that set. If there are several sets of values being animated, which is +     * the case if PropertyValuesHOlder objects were set on the ValueAnimator, then the evaluator +     * is assigned just to the first PropertyValuesHolder object.</p> +     * +     * @param value the evaluator to be used this animation +     */ +    public void setEvaluator(TypeEvaluator value) { +        if (value != null && mValues != null && mValues.length > 0) { +            mValues[0].setEvaluator(value); +        } +    } + +    /** +     * Start the animation playing. This version of start() takes a boolean flag that indicates +     * whether the animation should play in reverse. The flag is usually false, but may be set +     * to true if called from the reverse() method. +     * +     * <p>The animation started by calling this method will be run on the thread that called +     * this method. This thread should have a Looper on it (a runtime exception will be thrown if +     * this is not the case). Also, if the animation will animate +     * properties of objects in the view hierarchy, then the calling thread should be the UI +     * thread for that view hierarchy.</p> +     * +     * @param playBackwards Whether the ValueAnimator should start playing in reverse. +     */ +    private void start(boolean playBackwards) { +        if (Looper.myLooper() == null) { +            throw new AndroidRuntimeException("Animators may only be run on Looper threads"); +        } +        mPlayingBackwards = playBackwards; +        mCurrentIteration = 0; +        mPlayingState = STOPPED; +        mStarted = true; +        mStartedDelay = false; +        sPendingAnimations.get().add(this); +        if (mStartDelay == 0) { +            // This sets the initial value of the animation, prior to actually starting it running +            setCurrentPlayTime(getCurrentPlayTime()); +            mPlayingState = STOPPED; +            mRunning = true; + +            if (mListeners != null) { +                ArrayList<AnimatorListener> tmpListeners = +                        (ArrayList<AnimatorListener>) mListeners.clone(); +                int numListeners = tmpListeners.size(); +                for (int i = 0; i < numListeners; ++i) { +                    tmpListeners.get(i).onAnimationStart(this); +                } +            } +        } +        AnimationHandler animationHandler = sAnimationHandler.get(); +        if (animationHandler == null) { +            animationHandler = new AnimationHandler(); +            sAnimationHandler.set(animationHandler); +        } +        animationHandler.sendEmptyMessage(ANIMATION_START); +    } + +    @Override +    public void start() { +        start(false); +    } + +    @Override +    public void cancel() { +        // Only cancel if the animation is actually running or has been started and is about +        // to run +        if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) || +                sDelayedAnims.get().contains(this)) { +            // Only notify listeners if the animator has actually started +            if (mRunning && mListeners != null) { +                ArrayList<AnimatorListener> tmpListeners = +                        (ArrayList<AnimatorListener>) mListeners.clone(); +                for (AnimatorListener listener : tmpListeners) { +                    listener.onAnimationCancel(this); +                } +            } +            endAnimation(); +        } +    } + +    @Override +    public void end() { +        if (!sAnimations.get().contains(this) && !sPendingAnimations.get().contains(this)) { +            // Special case if the animation has not yet started; get it ready for ending +            mStartedDelay = false; +            startAnimation(); +        } else if (!mInitialized) { +            initAnimation(); +        } +        // The final value set on the target varies, depending on whether the animation +        // was supposed to repeat an odd number of times +        if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) { +            animateValue(0f); +        } else { +            animateValue(1f); +        } +        endAnimation(); +    } + +    @Override +    public boolean isRunning() { +        return (mPlayingState == RUNNING || mRunning); +    } + +    @Override +    public boolean isStarted() { +        return mStarted; +    } + +    /** +     * Plays the ValueAnimator in reverse. If the animation is already running, +     * it will stop itself and play backwards from the point reached when reverse was called. +     * If the animation is not currently running, then it will start from the end and +     * play backwards. This behavior is only set for the current animation; future playing +     * of the animation will use the default behavior of playing forward. +     */ +    public void reverse() { +        mPlayingBackwards = !mPlayingBackwards; +        if (mPlayingState == RUNNING) { +            long currentTime = AnimationUtils.currentAnimationTimeMillis(); +            long currentPlayTime = currentTime - mStartTime; +            long timeLeft = mDuration - currentPlayTime; +            mStartTime = currentTime - timeLeft; +        } else { +            start(true); +        } +    } + +    /** +     * Called internally to end an animation by removing it from the animations list. Must be +     * called on the UI thread. +     */ +    private void endAnimation() { +        sAnimations.get().remove(this); +        sPendingAnimations.get().remove(this); +        sDelayedAnims.get().remove(this); +        mPlayingState = STOPPED; +        if (mRunning && mListeners != null) { +            ArrayList<AnimatorListener> tmpListeners = +                    (ArrayList<AnimatorListener>) mListeners.clone(); +            int numListeners = tmpListeners.size(); +            for (int i = 0; i < numListeners; ++i) { +                tmpListeners.get(i).onAnimationEnd(this); +            } +        } +        mRunning = false; +        mStarted = false; +    } + +    /** +     * Called internally to start an animation by adding it to the active animations list. Must be +     * called on the UI thread. +     */ +    private void startAnimation() { +        initAnimation(); +        sAnimations.get().add(this); +        if (mStartDelay > 0 && mListeners != null) { +            // Listeners were already notified in start() if startDelay is 0; this is +            // just for delayed animations +            ArrayList<AnimatorListener> tmpListeners = +                    (ArrayList<AnimatorListener>) mListeners.clone(); +            int numListeners = tmpListeners.size(); +            for (int i = 0; i < numListeners; ++i) { +                tmpListeners.get(i).onAnimationStart(this); +            } +        } +    } + +    /** +     * Internal function called to process an animation frame on an animation that is currently +     * sleeping through its <code>startDelay</code> phase. The return value indicates whether it +     * should be woken up and put on the active animations queue. +     * +     * @param currentTime The current animation time, used to calculate whether the animation +     * has exceeded its <code>startDelay</code> and should be started. +     * @return True if the animation's <code>startDelay</code> has been exceeded and the animation +     * should be added to the set of active animations. +     */ +    private boolean delayedAnimationFrame(long currentTime) { +        if (!mStartedDelay) { +            mStartedDelay = true; +            mDelayStartTime = currentTime; +        } else { +            long deltaTime = currentTime - mDelayStartTime; +            if (deltaTime > mStartDelay) { +                // startDelay ended - start the anim and record the +                // mStartTime appropriately +                mStartTime = currentTime - (deltaTime - mStartDelay); +                mPlayingState = RUNNING; +                return true; +            } +        } +        return false; +    } + +    /** +     * This internal function processes a single animation frame for a given animation. The +     * currentTime parameter is the timing pulse sent by the handler, used to calculate the +     * elapsed duration, and therefore +     * the elapsed fraction, of the animation. The return value indicates whether the animation +     * should be ended (which happens when the elapsed time of the animation exceeds the +     * animation's duration, including the repeatCount). +     * +     * @param currentTime The current time, as tracked by the static timing handler +     * @return true if the animation's duration, including any repetitions due to +     * <code>repeatCount</code> has been exceeded and the animation should be ended. +     */ +    boolean animationFrame(long currentTime) { +        boolean done = false; + +        if (mPlayingState == STOPPED) { +            mPlayingState = RUNNING; +            if (mSeekTime < 0) { +                mStartTime = currentTime; +            } else { +                mStartTime = currentTime - mSeekTime; +                // Now that we're playing, reset the seek time +                mSeekTime = -1; +            } +        } +        switch (mPlayingState) { +        case RUNNING: +        case SEEKED: +            float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f; +            if (fraction >= 1f) { +                if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) { +                    // Time to repeat +                    if (mListeners != null) { +                        int numListeners = mListeners.size(); +                        for (int i = 0; i < numListeners; ++i) { +                            mListeners.get(i).onAnimationRepeat(this); +                        } +                    } +                    if (mRepeatMode == REVERSE) { +                        mPlayingBackwards = mPlayingBackwards ? false : true; +                    } +                    mCurrentIteration += (int)fraction; +                    fraction = fraction % 1f; +                    mStartTime += mDuration; +                } else { +                    done = true; +                    fraction = Math.min(fraction, 1.0f); +                } +            } +            if (mPlayingBackwards) { +                fraction = 1f - fraction; +            } +            animateValue(fraction); +            break; +        } + +        return done; +    } + +    /** +     * Returns the current animation fraction, which is the elapsed/interpolated fraction used in +     * the most recent frame update on the animation. +     * +     * @return Elapsed/interpolated fraction of the animation. +     */ +    public float getAnimatedFraction() { +        return mCurrentFraction; +    } + +    /** +     * This method is called with the elapsed fraction of the animation during every +     * animation frame. This function turns the elapsed fraction into an interpolated fraction +     * and then into an animated value (from the evaluator. The function is called mostly during +     * animation updates, but it is also called when the <code>end()</code> +     * function is called, to set the final value on the property. +     * +     * <p>Overrides of this method must call the superclass to perform the calculation +     * of the animated value.</p> +     * +     * @param fraction The elapsed fraction of the animation. +     */ +    void animateValue(float fraction) { +        fraction = mInterpolator.getInterpolation(fraction); +        mCurrentFraction = fraction; +        int numValues = mValues.length; +        for (int i = 0; i < numValues; ++i) { +            mValues[i].calculateValue(fraction); +        } +        if (mUpdateListeners != null) { +            int numListeners = mUpdateListeners.size(); +            for (int i = 0; i < numListeners; ++i) { +                mUpdateListeners.get(i).onAnimationUpdate(this); +            } +        } +    } + +    @Override +    public ValueAnimator clone() { +        final ValueAnimator anim = (ValueAnimator) super.clone(); +        if (mUpdateListeners != null) { +            ArrayList<AnimatorUpdateListener> oldListeners = mUpdateListeners; +            anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>(); +            int numListeners = oldListeners.size(); +            for (int i = 0; i < numListeners; ++i) { +                anim.mUpdateListeners.add(oldListeners.get(i)); +            } +        } +        anim.mSeekTime = -1; +        anim.mPlayingBackwards = false; +        anim.mCurrentIteration = 0; +        anim.mInitialized = false; +        anim.mPlayingState = STOPPED; +        anim.mStartedDelay = false; +        PropertyValuesHolder[] oldValues = mValues; +        if (oldValues != null) { +            int numValues = oldValues.length; +            anim.mValues = new PropertyValuesHolder[numValues]; +            anim.mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues); +            for (int i = 0; i < numValues; ++i) { +                PropertyValuesHolder newValuesHolder = oldValues[i].clone(); +                anim.mValues[i] = newValuesHolder; +                anim.mValuesMap.put(newValuesHolder.getPropertyName(), newValuesHolder); +            } +        } +        return anim; +    } + +    /** +     * Implementors of this interface can add themselves as update listeners +     * to an <code>ValueAnimator</code> instance to receive callbacks on every animation +     * frame, after the current frame's values have been calculated for that +     * <code>ValueAnimator</code>. +     */ +    public static interface AnimatorUpdateListener { +        /** +         * <p>Notifies the occurrence of another frame of the animation.</p> +         * +         * @param animation The animation which was repeated. +         */ +        void onAnimationUpdate(ValueAnimator animation); + +    } + +    /** +     * Return the number of animations currently running. +     * +     * Used by StrictMode internally to annotate violations.  Only +     * called on the main thread. +     * +     * @hide +     */ +    public static int getCurrentAnimationsCount() { +        return sAnimations.get().size(); +    } + +    /** +     * Clear all animations on this thread, without canceling or ending them. +     * This should be used with caution. +     * +     * @hide +     */ +    public static void clearAllAnimations() { +        sAnimations.get().clear(); +        sPendingAnimations.get().clear(); +        sDelayedAnims.get().clear(); +    } + +    @Override +    public String toString() { +        String returnVal = "ValueAnimator@" + Integer.toHexString(hashCode()); +        if (mValues != null) { +            for (int i = 0; i < mValues.length; ++i) { +                returnVal += "\n    " + mValues[i].toString(); +            } +        } +        return returnVal; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java new file mode 100644 index 000000000..7b830b9c0 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java @@ -0,0 +1,79 @@ +package com.actionbarsherlock.internal.nineoldandroids.view; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.ViewGroup; + +import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy; + +public abstract class NineViewGroup extends ViewGroup { +    private final AnimatorProxy mProxy; + +    public NineViewGroup(Context context) { +        super(context); +        mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null; +    } +    public NineViewGroup(Context context, AttributeSet attrs) { +        super(context, attrs); +        mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null; +    } +    public NineViewGroup(Context context, AttributeSet attrs, int defStyle) { +        super(context, attrs, defStyle); +        mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null; +    } + +    @Override +    public void setVisibility(int visibility) { +        if (mProxy != null) { +            if (visibility == GONE) { +                clearAnimation(); +            } else if (visibility == VISIBLE) { +                setAnimation(mProxy); +            } +        } +        super.setVisibility(visibility); +    } + +    public float getAlpha() { +        if (AnimatorProxy.NEEDS_PROXY) { +            return mProxy.getAlpha(); +        } else { +            return super.getAlpha(); +        } +    } +    public void setAlpha(float alpha) { +        if (AnimatorProxy.NEEDS_PROXY) { +            mProxy.setAlpha(alpha); +        } else { +            super.setAlpha(alpha); +        } +    } +    public float getTranslationX() { +        if (AnimatorProxy.NEEDS_PROXY) { +            return mProxy.getTranslationX(); +        } else { +            return super.getTranslationX(); +        } +    } +    public void setTranslationX(float translationX) { +        if (AnimatorProxy.NEEDS_PROXY) { +            mProxy.setTranslationX(translationX); +        } else { +            super.setTranslationX(translationX); +        } +    } +    public float getTranslationY() { +        if (AnimatorProxy.NEEDS_PROXY) { +            return mProxy.getTranslationY(); +        } else { +            return super.getTranslationY(); +        } +    } +    public void setTranslationY(float translationY) { +        if (AnimatorProxy.NEEDS_PROXY) { +            mProxy.setTranslationY(translationY); +        } else { +            super.setTranslationY(translationY); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java new file mode 100644 index 000000000..067d0494e --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java @@ -0,0 +1,212 @@ +package com.actionbarsherlock.internal.nineoldandroids.view.animation; + +import java.lang.ref.WeakReference; +import java.util.WeakHashMap; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.os.Build; +import android.util.FloatMath; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.Transformation; + +public final class AnimatorProxy extends Animation { +    public static final boolean NEEDS_PROXY = Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB; + +    private static final WeakHashMap<View, AnimatorProxy> PROXIES = +            new WeakHashMap<View, AnimatorProxy>(); + +    public static AnimatorProxy wrap(View view) { +        AnimatorProxy proxy = PROXIES.get(view); +        if (proxy == null) { +            proxy = new AnimatorProxy(view); +            PROXIES.put(view, proxy); +        } +        return proxy; +    } + +    private final WeakReference<View> mView; + +    private float mAlpha = 1; +    private float mScaleX = 1; +    private float mScaleY = 1; +    private float mTranslationX; +    private float mTranslationY; + +    private final RectF mBefore = new RectF(); +    private final RectF mAfter = new RectF(); +    private final Matrix mTempMatrix = new Matrix(); + +    private AnimatorProxy(View view) { +        setDuration(0); //perform transformation immediately +        setFillAfter(true); //persist transformation beyond duration +        view.setAnimation(this); +        mView = new WeakReference<View>(view); +    } + +    public float getAlpha() { +        return mAlpha; +    } +    public void setAlpha(float alpha) { +        if (mAlpha != alpha) { +            mAlpha = alpha; +            View view = mView.get(); +            if (view != null) { +                view.invalidate(); +            } +        } +    } +    public float getScaleX() { +        return mScaleX; +    } +    public void setScaleX(float scaleX) { +        if (mScaleX != scaleX) { +            prepareForUpdate(); +            mScaleX = scaleX; +            invalidateAfterUpdate(); +        } +    } +    public float getScaleY() { +        return mScaleY; +    } +    public void setScaleY(float scaleY) { +        if (mScaleY != scaleY) { +            prepareForUpdate(); +            mScaleY = scaleY; +            invalidateAfterUpdate(); +        } +    } +    public int getScrollX() { +        View view = mView.get(); +        if (view == null) { +            return 0; +        } +        return view.getScrollX(); +    } +    public void setScrollX(int value) { +        View view = mView.get(); +        if (view != null) { +            view.scrollTo(value, view.getScrollY()); +        } +    } +    public int getScrollY() { +        View view = mView.get(); +        if (view == null) { +            return 0; +        } +        return view.getScrollY(); +    } +    public void setScrollY(int value) { +        View view = mView.get(); +        if (view != null) { +            view.scrollTo(view.getScrollY(), value); +        } +    } + +    public float getTranslationX() { +        return mTranslationX; +    } +    public void setTranslationX(float translationX) { +        if (mTranslationX != translationX) { +            prepareForUpdate(); +            mTranslationX = translationX; +            invalidateAfterUpdate(); +        } +    } +    public float getTranslationY() { +        return mTranslationY; +    } +    public void setTranslationY(float translationY) { +        if (mTranslationY != translationY) { +            prepareForUpdate(); +            mTranslationY = translationY; +            invalidateAfterUpdate(); +        } +    } + +    private void prepareForUpdate() { +        View view = mView.get(); +        if (view != null) { +            computeRect(mBefore, view); +        } +    } +    private void invalidateAfterUpdate() { +        View view = mView.get(); +        if (view == null) { +            return; +        } +        View parent = (View)view.getParent(); +        if (parent == null) { +            return; +        } + +        view.setAnimation(this); + +        final RectF after = mAfter; +        computeRect(after, view); +        after.union(mBefore); + +        parent.invalidate( +                (int) FloatMath.floor(after.left), +                (int) FloatMath.floor(after.top), +                (int) FloatMath.ceil(after.right), +                (int) FloatMath.ceil(after.bottom)); +    } + +    private void computeRect(final RectF r, View view) { +        // compute current rectangle according to matrix transformation +        final float w = view.getWidth(); +        final float h = view.getHeight(); + +        // use a rectangle at 0,0 to make sure we don't run into issues with scaling +        r.set(0, 0, w, h); + +        final Matrix m = mTempMatrix; +        m.reset(); +        transformMatrix(m, view); +        mTempMatrix.mapRect(r); + +        r.offset(view.getLeft(), view.getTop()); + +        // Straighten coords if rotations flipped them +        if (r.right < r.left) { +            final float f = r.right; +            r.right = r.left; +            r.left = f; +        } +        if (r.bottom < r.top) { +            final float f = r.top; +            r.top = r.bottom; +            r.bottom = f; +        } +    } + +    private void transformMatrix(Matrix m, View view) { +        final float w = view.getWidth(); +        final float h = view.getHeight(); + +        final float sX = mScaleX; +        final float sY = mScaleY; +        if ((sX != 1.0f) || (sY != 1.0f)) { +            final float deltaSX = ((sX * w) - w) / 2f; +            final float deltaSY = ((sY * h) - h) / 2f; +            m.postScale(sX, sY); +            m.postTranslate(-deltaSX, -deltaSY); +        } +        m.postTranslate(mTranslationX, mTranslationY); +    } + +    @Override +    protected void applyTransformation(float interpolatedTime, Transformation t) { +        View view = mView.get(); +        if (view != null) { +            t.setAlpha(mAlpha); +            transformMatrix(t.getMatrix(), view); +        } +    } + +    @Override +    public void reset() { +        /* Do nothing. */ +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java new file mode 100644 index 000000000..953e3e844 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java @@ -0,0 +1,57 @@ +package com.actionbarsherlock.internal.nineoldandroids.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy; + +public class NineFrameLayout extends FrameLayout { +    private final AnimatorProxy mProxy; + +    public NineFrameLayout(Context context, AttributeSet attrs) { +        super(context, attrs); +        mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null; +    } + +    @Override +    public void setVisibility(int visibility) { +        if (mProxy != null) { +            if (visibility == GONE) { +                clearAnimation(); +            } else if (visibility == VISIBLE) { +                setAnimation(mProxy); +            } +        } +        super.setVisibility(visibility); +    } + +    public float getAlpha() { +        if (AnimatorProxy.NEEDS_PROXY) { +            return mProxy.getAlpha(); +        } else { +            return super.getAlpha(); +        } +    } +    public void setAlpha(float alpha) { +        if (AnimatorProxy.NEEDS_PROXY) { +            mProxy.setAlpha(alpha); +        } else { +            super.setAlpha(alpha); +        } +    } +    public float getTranslationY() { +        if (AnimatorProxy.NEEDS_PROXY) { +            return mProxy.getTranslationY(); +        } else { +            return super.getTranslationY(); +        } +    } +    public void setTranslationY(float translationY) { +        if (AnimatorProxy.NEEDS_PROXY) { +            mProxy.setTranslationY(translationY); +        } else { +            super.setTranslationY(translationY); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java new file mode 100644 index 000000000..129b5aaaa --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java @@ -0,0 +1,41 @@ +package com.actionbarsherlock.internal.nineoldandroids.widget; + +import android.content.Context; +import android.widget.HorizontalScrollView; +import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy; + +public class NineHorizontalScrollView extends HorizontalScrollView { +    private final AnimatorProxy mProxy; + +    public NineHorizontalScrollView(Context context) { +        super(context); +        mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null; +    } + +    @Override +    public void setVisibility(int visibility) { +        if (mProxy != null) { +            if (visibility == GONE) { +                clearAnimation(); +            } else if (visibility == VISIBLE) { +                setAnimation(mProxy); +            } +        } +        super.setVisibility(visibility); +    } + +    public float getAlpha() { +        if (AnimatorProxy.NEEDS_PROXY) { +            return mProxy.getAlpha(); +        } else { +            return super.getAlpha(); +        } +    } +    public void setAlpha(float alpha) { +        if (AnimatorProxy.NEEDS_PROXY) { +            mProxy.setAlpha(alpha); +        } else { +            super.setAlpha(alpha); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java new file mode 100644 index 000000000..1f381013a --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java @@ -0,0 +1,57 @@ +package com.actionbarsherlock.internal.nineoldandroids.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.LinearLayout; + +import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy; + +public class NineLinearLayout extends LinearLayout { +    private final AnimatorProxy mProxy; + +    public NineLinearLayout(Context context, AttributeSet attrs) { +        super(context, attrs); +        mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null; +    } + +    @Override +    public void setVisibility(int visibility) { +        if (mProxy != null) { +            if (visibility == GONE) { +                clearAnimation(); +            } else if (visibility == VISIBLE) { +                setAnimation(mProxy); +            } +        } +        super.setVisibility(visibility); +    } + +    public float getAlpha() { +        if (AnimatorProxy.NEEDS_PROXY) { +            return mProxy.getAlpha(); +        } else { +            return super.getAlpha(); +        } +    } +    public void setAlpha(float alpha) { +        if (AnimatorProxy.NEEDS_PROXY) { +            mProxy.setAlpha(alpha); +        } else { +            super.setAlpha(alpha); +        } +    } +    public float getTranslationX() { +        if (AnimatorProxy.NEEDS_PROXY) { +            return mProxy.getTranslationX(); +        } else { +            return super.getTranslationX(); +        } +    } +    public void setTranslationX(float translationX) { +        if (AnimatorProxy.NEEDS_PROXY) { +            mProxy.setTranslationX(translationX); +        } else { +            super.setTranslationX(translationX); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/ActionProviderWrapper.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/ActionProviderWrapper.java new file mode 100644 index 000000000..b136d50f0 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/ActionProviderWrapper.java @@ -0,0 +1,40 @@ +package com.actionbarsherlock.internal.view; + +import com.actionbarsherlock.internal.view.menu.SubMenuWrapper; +import com.actionbarsherlock.view.ActionProvider; +import android.view.View; + +public class ActionProviderWrapper extends android.view.ActionProvider { +    private final ActionProvider mProvider; + + +    public ActionProviderWrapper(ActionProvider provider) { +        super(null/*TODO*/); //XXX this *should* be unused +        mProvider = provider; +    } + + +    public ActionProvider unwrap() { +        return mProvider; +    } + +    @Override +    public View onCreateActionView() { +        return mProvider.onCreateActionView(); +    } + +    @Override +    public boolean hasSubMenu() { +        return mProvider.hasSubMenu(); +    } + +    @Override +    public boolean onPerformDefaultAction() { +        return mProvider.onPerformDefaultAction(); +    } + +    @Override +    public void onPrepareSubMenu(android.view.SubMenu subMenu) { +        mProvider.onPrepareSubMenu(new SubMenuWrapper(subMenu)); +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/StandaloneActionMode.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/StandaloneActionMode.java new file mode 100644 index 000000000..0a87bd3f7 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/StandaloneActionMode.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.view; + +import android.content.Context; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; + +import java.lang.ref.WeakReference; + +import com.actionbarsherlock.internal.view.menu.MenuBuilder; +import com.actionbarsherlock.internal.view.menu.MenuPopupHelper; +import com.actionbarsherlock.internal.view.menu.SubMenuBuilder; +import com.actionbarsherlock.internal.widget.ActionBarContextView; +import com.actionbarsherlock.view.ActionMode; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuInflater; +import com.actionbarsherlock.view.MenuItem; + +public class StandaloneActionMode extends ActionMode implements MenuBuilder.Callback { +    private Context mContext; +    private ActionBarContextView mContextView; +    private ActionMode.Callback mCallback; +    private WeakReference<View> mCustomView; +    private boolean mFinished; +    private boolean mFocusable; + +    private MenuBuilder mMenu; + +    public StandaloneActionMode(Context context, ActionBarContextView view, +            ActionMode.Callback callback, boolean isFocusable) { +        mContext = context; +        mContextView = view; +        mCallback = callback; + +        mMenu = new MenuBuilder(context).setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); +        mMenu.setCallback(this); +        mFocusable = isFocusable; +    } + +    @Override +    public void setTitle(CharSequence title) { +        mContextView.setTitle(title); +    } + +    @Override +    public void setSubtitle(CharSequence subtitle) { +        mContextView.setSubtitle(subtitle); +    } + +    @Override +    public void setTitle(int resId) { +        setTitle(mContext.getString(resId)); +    } + +    @Override +    public void setSubtitle(int resId) { +        setSubtitle(mContext.getString(resId)); +    } + +    @Override +    public void setCustomView(View view) { +        mContextView.setCustomView(view); +        mCustomView = view != null ? new WeakReference<View>(view) : null; +    } + +    @Override +    public void invalidate() { +        mCallback.onPrepareActionMode(this, mMenu); +    } + +    @Override +    public void finish() { +        if (mFinished) { +            return; +        } +        mFinished = true; + +        mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); +        mCallback.onDestroyActionMode(this); +    } + +    @Override +    public Menu getMenu() { +        return mMenu; +    } + +    @Override +    public CharSequence getTitle() { +        return mContextView.getTitle(); +    } + +    @Override +    public CharSequence getSubtitle() { +        return mContextView.getSubtitle(); +    } + +    @Override +    public View getCustomView() { +        return mCustomView != null ? mCustomView.get() : null; +    } + +    @Override +    public MenuInflater getMenuInflater() { +        return new MenuInflater(mContext); +    } + +    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { +        return mCallback.onActionItemClicked(this, item); +    } + +    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { +    } + +    public boolean onSubMenuSelected(SubMenuBuilder subMenu) { +        if (!subMenu.hasVisibleItems()) { +            return true; +        } + +        new MenuPopupHelper(mContext, subMenu).show(); +        return true; +    } + +    public void onCloseSubMenu(SubMenuBuilder menu) { +    } + +    public void onMenuModeChange(MenuBuilder menu) { +        invalidate(); +        mContextView.showOverflowMenu(); +    } + +    public boolean isUiFocusable() { +        return mFocusable; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_HasStateListenerSupport.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_HasStateListenerSupport.java new file mode 100644 index 000000000..7d45e81be --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_HasStateListenerSupport.java @@ -0,0 +1,6 @@ +package com.actionbarsherlock.internal.view; + +public interface View_HasStateListenerSupport { +    void addOnAttachStateChangeListener(View_OnAttachStateChangeListener listener); +    void removeOnAttachStateChangeListener(View_OnAttachStateChangeListener listener); +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_OnAttachStateChangeListener.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_OnAttachStateChangeListener.java new file mode 100644 index 000000000..3869d3290 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_OnAttachStateChangeListener.java @@ -0,0 +1,8 @@ +package com.actionbarsherlock.internal.view; + +import android.view.View; + +public interface View_OnAttachStateChangeListener { +    void onViewAttachedToWindow(View v); +    void onViewDetachedFromWindow(View v); +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java new file mode 100644 index 000000000..0354ad1ad --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.view.menu; + +import java.util.ArrayList; +import java.util.List; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.view.KeyEvent; + +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.SubMenu; + +/** + * @hide + */ +public class ActionMenu implements Menu { +    private Context mContext; + +    private boolean mIsQwerty; + +    private ArrayList<ActionMenuItem> mItems; + +    public ActionMenu(Context context) { +        mContext = context; +        mItems = new ArrayList<ActionMenuItem>(); +    } + +    public Context getContext() { +        return mContext; +    } + +    public MenuItem add(CharSequence title) { +        return add(0, 0, 0, title); +    } + +    public MenuItem add(int titleRes) { +        return add(0, 0, 0, titleRes); +    } + +    public MenuItem add(int groupId, int itemId, int order, int titleRes) { +        return add(groupId, itemId, order, mContext.getResources().getString(titleRes)); +    } + +    public MenuItem add(int groupId, int itemId, int order, CharSequence title) { +        ActionMenuItem item = new ActionMenuItem(getContext(), +                groupId, itemId, 0, order, title); +        mItems.add(order, item); +        return item; +    } + +    public int addIntentOptions(int groupId, int itemId, int order, +            ComponentName caller, Intent[] specifics, Intent intent, int flags, +            MenuItem[] outSpecificItems) { +        PackageManager pm = mContext.getPackageManager(); +        final List<ResolveInfo> lri = +                pm.queryIntentActivityOptions(caller, specifics, intent, 0); +        final int N = lri != null ? lri.size() : 0; + +        if ((flags & FLAG_APPEND_TO_GROUP) == 0) { +            removeGroup(groupId); +        } + +        for (int i=0; i<N; i++) { +            final ResolveInfo ri = lri.get(i); +            Intent rintent = new Intent( +                ri.specificIndex < 0 ? intent : specifics[ri.specificIndex]); +            rintent.setComponent(new ComponentName( +                    ri.activityInfo.applicationInfo.packageName, +                    ri.activityInfo.name)); +            final MenuItem item = add(groupId, itemId, order, ri.loadLabel(pm)) +                    .setIcon(ri.loadIcon(pm)) +                    .setIntent(rintent); +            if (outSpecificItems != null && ri.specificIndex >= 0) { +                outSpecificItems[ri.specificIndex] = item; +            } +        } + +        return N; +    } + +    public SubMenu addSubMenu(CharSequence title) { +        // TODO Implement submenus +        return null; +    } + +    public SubMenu addSubMenu(int titleRes) { +        // TODO Implement submenus +        return null; +    } + +    public SubMenu addSubMenu(int groupId, int itemId, int order, +            CharSequence title) { +        // TODO Implement submenus +        return null; +    } + +    public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) { +        // TODO Implement submenus +        return null; +    } + +    public void clear() { +        mItems.clear(); +    } + +    public void close() { +    } + +    private int findItemIndex(int id) { +        final ArrayList<ActionMenuItem> items = mItems; +        final int itemCount = items.size(); +        for (int i = 0; i < itemCount; i++) { +            if (items.get(i).getItemId() == id) { +                return i; +            } +        } + +        return -1; +    } + +    public MenuItem findItem(int id) { +        return mItems.get(findItemIndex(id)); +    } + +    public MenuItem getItem(int index) { +        return mItems.get(index); +    } + +    public boolean hasVisibleItems() { +        final ArrayList<ActionMenuItem> items = mItems; +        final int itemCount = items.size(); + +        for (int i = 0; i < itemCount; i++) { +            if (items.get(i).isVisible()) { +                return true; +            } +        } + +        return false; +    } + +    private ActionMenuItem findItemWithShortcut(int keyCode, KeyEvent event) { +        // TODO Make this smarter. +        final boolean qwerty = mIsQwerty; +        final ArrayList<ActionMenuItem> items = mItems; +        final int itemCount = items.size(); + +        for (int i = 0; i < itemCount; i++) { +            ActionMenuItem item = items.get(i); +            final char shortcut = qwerty ? item.getAlphabeticShortcut() : +                    item.getNumericShortcut(); +            if (keyCode == shortcut) { +                return item; +            } +        } +        return null; +    } + +    public boolean isShortcutKey(int keyCode, KeyEvent event) { +        return findItemWithShortcut(keyCode, event) != null; +    } + +    public boolean performIdentifierAction(int id, int flags) { +        final int index = findItemIndex(id); +        if (index < 0) { +            return false; +        } + +        return mItems.get(index).invoke(); +    } + +    public boolean performShortcut(int keyCode, KeyEvent event, int flags) { +        ActionMenuItem item = findItemWithShortcut(keyCode, event); +        if (item == null) { +            return false; +        } + +        return item.invoke(); +    } + +    public void removeGroup(int groupId) { +        final ArrayList<ActionMenuItem> items = mItems; +        int itemCount = items.size(); +        int i = 0; +        while (i < itemCount) { +            if (items.get(i).getGroupId() == groupId) { +                items.remove(i); +                itemCount--; +            } else { +                i++; +            } +        } +    } + +    public void removeItem(int id) { +        mItems.remove(findItemIndex(id)); +    } + +    public void setGroupCheckable(int group, boolean checkable, +            boolean exclusive) { +        final ArrayList<ActionMenuItem> items = mItems; +        final int itemCount = items.size(); + +        for (int i = 0; i < itemCount; i++) { +            ActionMenuItem item = items.get(i); +            if (item.getGroupId() == group) { +                item.setCheckable(checkable); +                item.setExclusiveCheckable(exclusive); +            } +        } +    } + +    public void setGroupEnabled(int group, boolean enabled) { +        final ArrayList<ActionMenuItem> items = mItems; +        final int itemCount = items.size(); + +        for (int i = 0; i < itemCount; i++) { +            ActionMenuItem item = items.get(i); +            if (item.getGroupId() == group) { +                item.setEnabled(enabled); +            } +        } +    } + +    public void setGroupVisible(int group, boolean visible) { +        final ArrayList<ActionMenuItem> items = mItems; +        final int itemCount = items.size(); + +        for (int i = 0; i < itemCount; i++) { +            ActionMenuItem item = items.get(i); +            if (item.getGroupId() == group) { +                item.setVisible(visible); +            } +        } +    } + +    public void setQwertyMode(boolean isQwerty) { +        mIsQwerty = isQwerty; +    } + +    public int size() { +        return mItems.size(); +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java new file mode 100644 index 000000000..510b97488 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.view.menu; + +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.View; + +import com.actionbarsherlock.view.ActionProvider; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.SubMenu; + +/** + * @hide + */ +public class ActionMenuItem implements MenuItem { +    private final int mId; +    private final int mGroup; +    //UNUSED private final int mCategoryOrder; +    private final int mOrdering; + +    private CharSequence mTitle; +    private CharSequence mTitleCondensed; +    private Intent mIntent; +    private char mShortcutNumericChar; +    private char mShortcutAlphabeticChar; + +    private Drawable mIconDrawable; +    //UNUSED private int mIconResId = NO_ICON; + +    private Context mContext; + +    private MenuItem.OnMenuItemClickListener mClickListener; + +    //UNUSED private static final int NO_ICON = 0; + +    private int mFlags = ENABLED; +    private static final int CHECKABLE      = 0x00000001; +    private static final int CHECKED        = 0x00000002; +    private static final int EXCLUSIVE      = 0x00000004; +    private static final int HIDDEN         = 0x00000008; +    private static final int ENABLED        = 0x00000010; + +    public ActionMenuItem(Context context, int group, int id, int categoryOrder, int ordering, +            CharSequence title) { +        mContext = context; +        mId = id; +        mGroup = group; +        //UNUSED mCategoryOrder = categoryOrder; +        mOrdering = ordering; +        mTitle = title; +    } + +    public char getAlphabeticShortcut() { +        return mShortcutAlphabeticChar; +    } + +    public int getGroupId() { +        return mGroup; +    } + +    public Drawable getIcon() { +        return mIconDrawable; +    } + +    public Intent getIntent() { +        return mIntent; +    } + +    public int getItemId() { +        return mId; +    } + +    public ContextMenuInfo getMenuInfo() { +        return null; +    } + +    public char getNumericShortcut() { +        return mShortcutNumericChar; +    } + +    public int getOrder() { +        return mOrdering; +    } + +    public SubMenu getSubMenu() { +        return null; +    } + +    public CharSequence getTitle() { +        return mTitle; +    } + +    public CharSequence getTitleCondensed() { +        return mTitleCondensed; +    } + +    public boolean hasSubMenu() { +        return false; +    } + +    public boolean isCheckable() { +        return (mFlags & CHECKABLE) != 0; +    } + +    public boolean isChecked() { +        return (mFlags & CHECKED) != 0; +    } + +    public boolean isEnabled() { +        return (mFlags & ENABLED) != 0; +    } + +    public boolean isVisible() { +        return (mFlags & HIDDEN) == 0; +    } + +    public MenuItem setAlphabeticShortcut(char alphaChar) { +        mShortcutAlphabeticChar = alphaChar; +        return this; +    } + +    public MenuItem setCheckable(boolean checkable) { +        mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0); +        return this; +    } + +    public ActionMenuItem setExclusiveCheckable(boolean exclusive) { +        mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0); +        return this; +    } + +    public MenuItem setChecked(boolean checked) { +        mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0); +        return this; +    } + +    public MenuItem setEnabled(boolean enabled) { +        mFlags = (mFlags & ~ENABLED) | (enabled ? ENABLED : 0); +        return this; +    } + +    public MenuItem setIcon(Drawable icon) { +        mIconDrawable = icon; +        //UNUSED mIconResId = NO_ICON; +        return this; +    } + +    public MenuItem setIcon(int iconRes) { +        //UNUSED mIconResId = iconRes; +        mIconDrawable = mContext.getResources().getDrawable(iconRes); +        return this; +    } + +    public MenuItem setIntent(Intent intent) { +        mIntent = intent; +        return this; +    } + +    public MenuItem setNumericShortcut(char numericChar) { +        mShortcutNumericChar = numericChar; +        return this; +    } + +    public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) { +        mClickListener = menuItemClickListener; +        return this; +    } + +    public MenuItem setShortcut(char numericChar, char alphaChar) { +        mShortcutNumericChar = numericChar; +        mShortcutAlphabeticChar = alphaChar; +        return this; +    } + +    public MenuItem setTitle(CharSequence title) { +        mTitle = title; +        return this; +    } + +    public MenuItem setTitle(int title) { +        mTitle = mContext.getResources().getString(title); +        return this; +    } + +    public MenuItem setTitleCondensed(CharSequence title) { +        mTitleCondensed = title; +        return this; +    } + +    public MenuItem setVisible(boolean visible) { +        mFlags = (mFlags & HIDDEN) | (visible ? 0 : HIDDEN); +        return this; +    } + +    public boolean invoke() { +        if (mClickListener != null && mClickListener.onMenuItemClick(this)) { +            return true; +        } + +        if (mIntent != null) { +            mContext.startActivity(mIntent); +            return true; +        } + +        return false; +    } + +    public void setShowAsAction(int show) { +        // Do nothing. ActionMenuItems always show as action buttons. +    } + +    public MenuItem setActionView(View actionView) { +        throw new UnsupportedOperationException(); +    } + +    public View getActionView() { +        return null; +    } + +    @Override +    public MenuItem setActionView(int resId) { +        throw new UnsupportedOperationException(); +    } + +    @Override +    public ActionProvider getActionProvider() { +        return null; +    } + +    @Override +    public MenuItem setActionProvider(ActionProvider actionProvider) { +        throw new UnsupportedOperationException(); +    } + +    @Override +    public MenuItem setShowAsActionFlags(int actionEnum) { +        setShowAsAction(actionEnum); +        return this; +    } + +    @Override +    public boolean expandActionView() { +        return false; +    } + +    @Override +    public boolean collapseActionView() { +        return false; +    } + +    @Override +    public boolean isActionViewExpanded() { +        return false; +    } + +    @Override +    public MenuItem setOnActionExpandListener(OnActionExpandListener listener) { +        // No need to save the listener; ActionMenuItem does not support collapsing items. +        return this; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java new file mode 100644 index 000000000..dcb50f362 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.view.menu; + +import java.util.HashSet; +import java.util.Set; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.Toast; + +import com.actionbarsherlock.R; +import com.actionbarsherlock.internal.view.View_HasStateListenerSupport; +import com.actionbarsherlock.internal.view.View_OnAttachStateChangeListener; +import com.actionbarsherlock.internal.widget.CapitalizingButton; + +import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean; + +/** + * @hide + */ +public class ActionMenuItemView extends LinearLayout +        implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener, +        ActionMenuView.ActionMenuChildView, View_HasStateListenerSupport { +    //UNUSED private static final String TAG = "ActionMenuItemView"; + +    private MenuItemImpl mItemData; +    private CharSequence mTitle; +    private MenuBuilder.ItemInvoker mItemInvoker; + +    private ImageButton mImageButton; +    private CapitalizingButton mTextButton; +    private boolean mAllowTextWithIcon; +    private boolean mExpandedFormat; +    private int mMinWidth; + +    private final Set<View_OnAttachStateChangeListener> mListeners = new HashSet<View_OnAttachStateChangeListener>(); + +    public ActionMenuItemView(Context context) { +        this(context, null); +    } + +    public ActionMenuItemView(Context context, AttributeSet attrs) { +        this(context, attrs, 0); +    } + +    public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) { +        //TODO super(context, attrs, defStyle); +        super(context, attrs); +        mAllowTextWithIcon = getResources_getBoolean(context, +                R.bool.abs__config_allowActionMenuItemTextWithIcon); +        TypedArray a = context.obtainStyledAttributes(attrs, +                R.styleable.SherlockActionMenuItemView, 0, 0); +        mMinWidth = a.getDimensionPixelSize( +                R.styleable.SherlockActionMenuItemView_android_minWidth, 0); +        a.recycle(); +    } + +    @Override +    public void addOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) { +        mListeners.add(listener); +    } + +    @Override +    public void removeOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) { +        mListeners.remove(listener); +    } + +    @Override +    protected void onAttachedToWindow() { +        super.onAttachedToWindow(); +        for (View_OnAttachStateChangeListener listener : mListeners) { +            listener.onViewAttachedToWindow(this); +        } +    } + +    @Override +    protected void onDetachedFromWindow() { +        super.onDetachedFromWindow(); +        for (View_OnAttachStateChangeListener listener : mListeners) { +            listener.onViewDetachedFromWindow(this); +        } +    } + +    @Override +    public void onFinishInflate() { + +        mImageButton = (ImageButton) findViewById(R.id.abs__imageButton); +        mTextButton = (CapitalizingButton) findViewById(R.id.abs__textButton); +        mImageButton.setOnClickListener(this); +        mTextButton.setOnClickListener(this); +        mImageButton.setOnLongClickListener(this); +        setOnClickListener(this); +        setOnLongClickListener(this); +    } + +    public MenuItemImpl getItemData() { +        return mItemData; +    } + +    public void initialize(MenuItemImpl itemData, int menuType) { +        mItemData = itemData; + +        setIcon(itemData.getIcon()); +        setTitle(itemData.getTitleForItemView(this)); // Title only takes effect if there is no icon +        setId(itemData.getItemId()); + +        setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE); +        setEnabled(itemData.isEnabled()); +    } + +    @Override +    public void setEnabled(boolean enabled) { +        super.setEnabled(enabled); +        mImageButton.setEnabled(enabled); +        mTextButton.setEnabled(enabled); +    } + +    public void onClick(View v) { +        if (mItemInvoker != null) { +            mItemInvoker.invokeItem(mItemData); +        } +    } + +    public void setItemInvoker(MenuBuilder.ItemInvoker invoker) { +        mItemInvoker = invoker; +    } + +    public boolean prefersCondensedTitle() { +        return true; +    } + +    public void setCheckable(boolean checkable) { +        // TODO Support checkable action items +    } + +    public void setChecked(boolean checked) { +        // TODO Support checkable action items +    } + +    public void setExpandedFormat(boolean expandedFormat) { +        if (mExpandedFormat != expandedFormat) { +            mExpandedFormat = expandedFormat; +            if (mItemData != null) { +                mItemData.actionFormatChanged(); +            } +        } +    } + +    private void updateTextButtonVisibility() { +        boolean visible = !TextUtils.isEmpty(mTextButton.getText()); +        visible &= mImageButton.getDrawable() == null || +                (mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat)); + +        mTextButton.setVisibility(visible ? VISIBLE : GONE); +    } + +    public void setIcon(Drawable icon) { +        mImageButton.setImageDrawable(icon); +        if (icon != null) { +            mImageButton.setVisibility(VISIBLE); +        } else { +            mImageButton.setVisibility(GONE); +        } + +        updateTextButtonVisibility(); +    } + +    public boolean hasText() { +        return mTextButton.getVisibility() != GONE; +    } + +    public void setShortcut(boolean showShortcut, char shortcutKey) { +        // Action buttons don't show text for shortcut keys. +    } + +    public void setTitle(CharSequence title) { +        mTitle = title; + +        mTextButton.setTextCompat(mTitle); + +        setContentDescription(mTitle); +        updateTextButtonVisibility(); +    } + +    @Override +    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { +        onPopulateAccessibilityEvent(event); +        return true; +    } + +    @Override +    public void onPopulateAccessibilityEvent(AccessibilityEvent event) { +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { +            super.onPopulateAccessibilityEvent(event); +        } +        final CharSequence cdesc = getContentDescription(); +        if (!TextUtils.isEmpty(cdesc)) { +            event.getText().add(cdesc); +        } +    } + +    @Override +    public boolean dispatchHoverEvent(MotionEvent event) { +        // Don't allow children to hover; we want this to be treated as a single component. +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { +            return onHoverEvent(event); +        } +        return false; +    } + +    public boolean showsIcon() { +        return true; +    } + +    public boolean needsDividerBefore() { +        return hasText() && mItemData.getIcon() == null; +    } + +    public boolean needsDividerAfter() { +        return hasText(); +    } + +    @Override +    public boolean onLongClick(View v) { +        if (hasText()) { +            // Don't show the cheat sheet for items that already show text. +            return false; +        } + +        final int[] screenPos = new int[2]; +        final Rect displayFrame = new Rect(); +        getLocationOnScreen(screenPos); +        getWindowVisibleDisplayFrame(displayFrame); + +        final Context context = getContext(); +        final int width = getWidth(); +        final int height = getHeight(); +        final int midy = screenPos[1] + height / 2; +        final int screenWidth = context.getResources().getDisplayMetrics().widthPixels; + +        Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT); +        if (midy < displayFrame.height()) { +            // Show along the top; follow action buttons +            cheatSheet.setGravity(Gravity.TOP | Gravity.RIGHT, +                    screenWidth - screenPos[0] - width / 2, height); +        } else { +            // Show along the bottom center +            cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height); +        } +        cheatSheet.show(); +        return true; +    } + +    @Override +    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        super.onMeasure(widthMeasureSpec, heightMeasureSpec); + +        final int widthMode = MeasureSpec.getMode(widthMeasureSpec); +        final int specSize = MeasureSpec.getSize(widthMeasureSpec); +        final int oldMeasuredWidth = getMeasuredWidth(); +        final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(specSize, mMinWidth) +                : mMinWidth; + +        if (widthMode != MeasureSpec.EXACTLY && mMinWidth > 0 && oldMeasuredWidth < targetWidth) { +            // Remeasure at exactly the minimum width. +            super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY), +                    heightMeasureSpec); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java new file mode 100644 index 000000000..876a22c58 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java @@ -0,0 +1,714 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.actionbarsherlock.internal.view.menu; + +import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getInteger; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.SparseBooleanArray; +import android.view.SoundEffectConstants; +import android.view.View; +import android.view.View.MeasureSpec; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.widget.ImageButton; +import com.actionbarsherlock.R; +import com.actionbarsherlock.internal.view.View_HasStateListenerSupport; +import com.actionbarsherlock.internal.view.View_OnAttachStateChangeListener; +import com.actionbarsherlock.internal.view.menu.ActionMenuView.ActionMenuChildView; +import com.actionbarsherlock.view.ActionProvider; +import com.actionbarsherlock.view.MenuItem; + +/** + * MenuPresenter for building action menus as seen in the action bar and action modes. + */ +public class ActionMenuPresenter extends BaseMenuPresenter +        implements ActionProvider.SubUiVisibilityListener { +    //UNUSED private static final String TAG = "ActionMenuPresenter"; + +    private View mOverflowButton; +    private boolean mReserveOverflow; +    private boolean mReserveOverflowSet; +    private int mWidthLimit; +    private int mActionItemWidthLimit; +    private int mMaxItems; +    private boolean mMaxItemsSet; +    private boolean mStrictWidthLimit; +    private boolean mWidthLimitSet; +    private boolean mExpandedActionViewsExclusive; + +    private int mMinCellSize; + +    // Group IDs that have been added as actions - used temporarily, allocated here for reuse. +    private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray(); + +    private View mScrapActionButtonView; + +    private OverflowPopup mOverflowPopup; +    private ActionButtonSubmenu mActionButtonPopup; + +    private OpenOverflowRunnable mPostedOpenRunnable; + +    final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback(); +    int mOpenSubMenuId; + +    public ActionMenuPresenter(Context context) { +        super(context, R.layout.abs__action_menu_layout, +                R.layout.abs__action_menu_item_layout); +    } + +    @Override +    public void initForMenu(Context context, MenuBuilder menu) { +        super.initForMenu(context, menu); + +        final Resources res = context.getResources(); + +        if (!mReserveOverflowSet) { +            mReserveOverflow = reserveOverflow(mContext); +        } + +        if (!mWidthLimitSet) { +            mWidthLimit = res.getDisplayMetrics().widthPixels / 2; +        } + +        // Measure for initial configuration +        if (!mMaxItemsSet) { +            mMaxItems = getResources_getInteger(context, R.integer.abs__max_action_buttons); +        } + +        int width = mWidthLimit; +        if (mReserveOverflow) { +            if (mOverflowButton == null) { +                mOverflowButton = new OverflowMenuButton(mSystemContext); +                final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); +                mOverflowButton.measure(spec, spec); +            } +            width -= mOverflowButton.getMeasuredWidth(); +        } else { +            mOverflowButton = null; +        } + +        mActionItemWidthLimit = width; + +        mMinCellSize = (int) (ActionMenuView.MIN_CELL_SIZE * res.getDisplayMetrics().density); + +        // Drop a scrap view as it may no longer reflect the proper context/config. +        mScrapActionButtonView = null; +    } + +    public static boolean reserveOverflow(Context context) { +        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { +            return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB); +        } else { +            return !HasPermanentMenuKey.get(context); +        } +    } + +    private static class HasPermanentMenuKey { +        public static boolean get(Context context) { +            return ViewConfiguration.get(context).hasPermanentMenuKey(); +        } +    } + +    public void onConfigurationChanged(Configuration newConfig) { +        if (!mMaxItemsSet) { +            mMaxItems = getResources_getInteger(mContext, +                    R.integer.abs__max_action_buttons); +            if (mMenu != null) { +                mMenu.onItemsChanged(true); +            } +        } +    } + +    public void setWidthLimit(int width, boolean strict) { +        mWidthLimit = width; +        mStrictWidthLimit = strict; +        mWidthLimitSet = true; +    } + +    public void setReserveOverflow(boolean reserveOverflow) { +        mReserveOverflow = reserveOverflow; +        mReserveOverflowSet = true; +    } + +    public void setItemLimit(int itemCount) { +        mMaxItems = itemCount; +        mMaxItemsSet = true; +    } + +    public void setExpandedActionViewsExclusive(boolean isExclusive) { +        mExpandedActionViewsExclusive = isExclusive; +    } + +    @Override +    public MenuView getMenuView(ViewGroup root) { +        MenuView result = super.getMenuView(root); +        ((ActionMenuView) result).setPresenter(this); +        return result; +    } + +    @Override +    public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) { +        View actionView = item.getActionView(); +        if (actionView == null || item.hasCollapsibleActionView()) { +            if (!(convertView instanceof ActionMenuItemView)) { +                convertView = null; +            } +            actionView = super.getItemView(item, convertView, parent); +        } +        actionView.setVisibility(item.isActionViewExpanded() ? View.GONE : View.VISIBLE); + +        final ActionMenuView menuParent = (ActionMenuView) parent; +        final ViewGroup.LayoutParams lp = actionView.getLayoutParams(); +        if (!menuParent.checkLayoutParams(lp)) { +            actionView.setLayoutParams(menuParent.generateLayoutParams(lp)); +        } +        return actionView; +    } + +    @Override +    public void bindItemView(MenuItemImpl item, MenuView.ItemView itemView) { +        itemView.initialize(item, 0); + +        final ActionMenuView menuView = (ActionMenuView) mMenuView; +        ActionMenuItemView actionItemView = (ActionMenuItemView) itemView; +        actionItemView.setItemInvoker(menuView); +    } + +    @Override +    public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) { +        return item.isActionButton(); +    } + +    @Override +    public void updateMenuView(boolean cleared) { +        super.updateMenuView(cleared); + +        if (mMenu != null) { +            final ArrayList<MenuItemImpl> actionItems = mMenu.getActionItems(); +            final int count = actionItems.size(); +            for (int i = 0; i < count; i++) { +                final ActionProvider provider = actionItems.get(i).getActionProvider(); +                if (provider != null) { +                    provider.setSubUiVisibilityListener(this); +                } +            } +        } + +        final ArrayList<MenuItemImpl> nonActionItems = mMenu != null ? +                mMenu.getNonActionItems() : null; + +        boolean hasOverflow = false; +        if (mReserveOverflow && nonActionItems != null) { +            final int count = nonActionItems.size(); +            if (count == 1) { +                hasOverflow = !nonActionItems.get(0).isActionViewExpanded(); +            } else { +                hasOverflow = count > 0; +            } +        } + +        if (hasOverflow) { +            if (mOverflowButton == null) { +                mOverflowButton = new OverflowMenuButton(mSystemContext); +            } +            ViewGroup parent = (ViewGroup) mOverflowButton.getParent(); +            if (parent != mMenuView) { +                if (parent != null) { +                    parent.removeView(mOverflowButton); +                } +                ActionMenuView menuView = (ActionMenuView) mMenuView; +                menuView.addView(mOverflowButton, menuView.generateOverflowButtonLayoutParams()); +            } +        } else if (mOverflowButton != null && mOverflowButton.getParent() == mMenuView) { +            ((ViewGroup) mMenuView).removeView(mOverflowButton); +        } + +        ((ActionMenuView) mMenuView).setOverflowReserved(mReserveOverflow); +    } + +    @Override +    public boolean filterLeftoverView(ViewGroup parent, int childIndex) { +        if (parent.getChildAt(childIndex) == mOverflowButton) return false; +        return super.filterLeftoverView(parent, childIndex); +    } + +    public boolean onSubMenuSelected(SubMenuBuilder subMenu) { +        if (!subMenu.hasVisibleItems()) return false; + +        SubMenuBuilder topSubMenu = subMenu; +        while (topSubMenu.getParentMenu() != mMenu) { +            topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu(); +        } +        View anchor = findViewForItem(topSubMenu.getItem()); +        if (anchor == null) { +            if (mOverflowButton == null) return false; +            anchor = mOverflowButton; +        } + +        mOpenSubMenuId = subMenu.getItem().getItemId(); +        mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu); +        mActionButtonPopup.setAnchorView(anchor); +        mActionButtonPopup.show(); +        super.onSubMenuSelected(subMenu); +        return true; +    } + +    private View findViewForItem(MenuItem item) { +        final ViewGroup parent = (ViewGroup) mMenuView; +        if (parent == null) return null; + +        final int count = parent.getChildCount(); +        for (int i = 0; i < count; i++) { +            final View child = parent.getChildAt(i); +            if (child instanceof MenuView.ItemView && +                    ((MenuView.ItemView) child).getItemData() == item) { +                return child; +            } +        } +        return null; +    } + +    /** +     * Display the overflow menu if one is present. +     * @return true if the overflow menu was shown, false otherwise. +     */ +    public boolean showOverflowMenu() { +        if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null && +                mPostedOpenRunnable == null && !mMenu.getNonActionItems().isEmpty()) { +            OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true); +            mPostedOpenRunnable = new OpenOverflowRunnable(popup); +            // Post this for later; we might still need a layout for the anchor to be right. +            ((View) mMenuView).post(mPostedOpenRunnable); + +            // ActionMenuPresenter uses null as a callback argument here +            // to indicate overflow is opening. +            super.onSubMenuSelected(null); + +            return true; +        } +        return false; +    } + +    /** +     * Hide the overflow menu if it is currently showing. +     * +     * @return true if the overflow menu was hidden, false otherwise. +     */ +    public boolean hideOverflowMenu() { +        if (mPostedOpenRunnable != null && mMenuView != null) { +            ((View) mMenuView).removeCallbacks(mPostedOpenRunnable); +            mPostedOpenRunnable = null; +            return true; +        } + +        MenuPopupHelper popup = mOverflowPopup; +        if (popup != null) { +            popup.dismiss(); +            return true; +        } +        return false; +    } + +    /** +     * Dismiss all popup menus - overflow and submenus. +     * @return true if popups were dismissed, false otherwise. (This can be because none were open.) +     */ +    public boolean dismissPopupMenus() { +        boolean result = hideOverflowMenu(); +        result |= hideSubMenus(); +        return result; +    } + +    /** +     * Dismiss all submenu popups. +     * +     * @return true if popups were dismissed, false otherwise. (This can be because none were open.) +     */ +    public boolean hideSubMenus() { +        if (mActionButtonPopup != null) { +            mActionButtonPopup.dismiss(); +            return true; +        } +        return false; +    } + +    /** +     * @return true if the overflow menu is currently showing +     */ +    public boolean isOverflowMenuShowing() { +        return mOverflowPopup != null && mOverflowPopup.isShowing(); +    } + +    /** +     * @return true if space has been reserved in the action menu for an overflow item. +     */ +    public boolean isOverflowReserved() { +        return mReserveOverflow; +    } + +    public boolean flagActionItems() { +        final ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems(); +        final int itemsSize = visibleItems.size(); +        int maxActions = mMaxItems; +        int widthLimit = mActionItemWidthLimit; +        final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); +        final ViewGroup parent = (ViewGroup) mMenuView; + +        int requiredItems = 0; +        int requestedItems = 0; +        int firstActionWidth = 0; +        boolean hasOverflow = false; +        for (int i = 0; i < itemsSize; i++) { +            MenuItemImpl item = visibleItems.get(i); +            if (item.requiresActionButton()) { +                requiredItems++; +            } else if (item.requestsActionButton()) { +                requestedItems++; +            } else { +                hasOverflow = true; +            } +            if (mExpandedActionViewsExclusive && item.isActionViewExpanded()) { +                // Overflow everything if we have an expanded action view and we're +                // space constrained. +                maxActions = 0; +            } +        } + +        // Reserve a spot for the overflow item if needed. +        if (mReserveOverflow && +                (hasOverflow || requiredItems + requestedItems > maxActions)) { +            maxActions--; +        } +        maxActions -= requiredItems; + +        final SparseBooleanArray seenGroups = mActionButtonGroups; +        seenGroups.clear(); + +        int cellSize = 0; +        int cellsRemaining = 0; +        if (mStrictWidthLimit) { +            cellsRemaining = widthLimit / mMinCellSize; +            final int cellSizeRemaining = widthLimit % mMinCellSize; +            cellSize = mMinCellSize + cellSizeRemaining / cellsRemaining; +        } + +        // Flag as many more requested items as will fit. +        for (int i = 0; i < itemsSize; i++) { +            MenuItemImpl item = visibleItems.get(i); + +            if (item.requiresActionButton()) { +                View v = getItemView(item, mScrapActionButtonView, parent); +                if (mScrapActionButtonView == null) { +                    mScrapActionButtonView = v; +                } +                if (mStrictWidthLimit) { +                    cellsRemaining -= ActionMenuView.measureChildForCells(v, +                            cellSize, cellsRemaining, querySpec, 0); +                } else { +                    v.measure(querySpec, querySpec); +                } +                final int measuredWidth = v.getMeasuredWidth(); +                widthLimit -= measuredWidth; +                if (firstActionWidth == 0) { +                    firstActionWidth = measuredWidth; +                } +                final int groupId = item.getGroupId(); +                if (groupId != 0) { +                    seenGroups.put(groupId, true); +                } +                item.setIsActionButton(true); +            } else if (item.requestsActionButton()) { +                // Items in a group with other items that already have an action slot +                // can break the max actions rule, but not the width limit. +                final int groupId = item.getGroupId(); +                final boolean inGroup = seenGroups.get(groupId); +                boolean isAction = (maxActions > 0 || inGroup) && widthLimit > 0 && +                        (!mStrictWidthLimit || cellsRemaining > 0); + +                if (isAction) { +                    View v = getItemView(item, mScrapActionButtonView, parent); +                    if (mScrapActionButtonView == null) { +                        mScrapActionButtonView = v; +                    } +                    if (mStrictWidthLimit) { +                        final int cells = ActionMenuView.measureChildForCells(v, +                                cellSize, cellsRemaining, querySpec, 0); +                        cellsRemaining -= cells; +                        if (cells == 0) { +                            isAction = false; +                        } +                    } else { +                        v.measure(querySpec, querySpec); +                    } +                    final int measuredWidth = v.getMeasuredWidth(); +                    widthLimit -= measuredWidth; +                    if (firstActionWidth == 0) { +                        firstActionWidth = measuredWidth; +                    } + +                    if (mStrictWidthLimit) { +                        isAction &= widthLimit >= 0; +                    } else { +                        // Did this push the entire first item past the limit? +                        isAction &= widthLimit + firstActionWidth > 0; +                    } +                } + +                if (isAction && groupId != 0) { +                    seenGroups.put(groupId, true); +                } else if (inGroup) { +                    // We broke the width limit. Demote the whole group, they all overflow now. +                    seenGroups.put(groupId, false); +                    for (int j = 0; j < i; j++) { +                        MenuItemImpl areYouMyGroupie = visibleItems.get(j); +                        if (areYouMyGroupie.getGroupId() == groupId) { +                            // Give back the action slot +                            if (areYouMyGroupie.isActionButton()) maxActions++; +                            areYouMyGroupie.setIsActionButton(false); +                        } +                    } +                } + +                if (isAction) maxActions--; + +                item.setIsActionButton(isAction); +            } +        } +        return true; +    } + +    @Override +    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { +        dismissPopupMenus(); +        super.onCloseMenu(menu, allMenusAreClosing); +    } + +    @Override +    public Parcelable onSaveInstanceState() { +        SavedState state = new SavedState(); +        state.openSubMenuId = mOpenSubMenuId; +        return state; +    } + +    @Override +    public void onRestoreInstanceState(Parcelable state) { +        SavedState saved = (SavedState) state; +        if (saved.openSubMenuId > 0) { +            MenuItem item = mMenu.findItem(saved.openSubMenuId); +            if (item != null) { +                SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu(); +                onSubMenuSelected(subMenu); +            } +        } +    } + +    @Override +    public void onSubUiVisibilityChanged(boolean isVisible) { +        if (isVisible) { +            // Not a submenu, but treat it like one. +            super.onSubMenuSelected(null); +        } else { +            mMenu.close(false); +        } +    } + +    private static class SavedState implements Parcelable { +        public int openSubMenuId; + +        SavedState() { +        } + +        SavedState(Parcel in) { +            openSubMenuId = in.readInt(); +        } + +        @Override +        public int describeContents() { +            return 0; +        } + +        @Override +        public void writeToParcel(Parcel dest, int flags) { +            dest.writeInt(openSubMenuId); +        } + +        @SuppressWarnings("unused") +        public static final Parcelable.Creator<SavedState> CREATOR +                = new Parcelable.Creator<SavedState>() { +            public SavedState createFromParcel(Parcel in) { +                return new SavedState(in); +            } + +            public SavedState[] newArray(int size) { +                return new SavedState[size]; +            } +        }; +    } + +    private class OverflowMenuButton extends ImageButton implements ActionMenuChildView, View_HasStateListenerSupport { +        private final Set<View_OnAttachStateChangeListener> mListeners = new HashSet<View_OnAttachStateChangeListener>(); + +        public OverflowMenuButton(Context context) { +            super(context, null, R.attr.actionOverflowButtonStyle); + +            setClickable(true); +            setFocusable(true); +            setVisibility(VISIBLE); +            setEnabled(true); +        } + +        @Override +        public boolean performClick() { +            if (super.performClick()) { +                return true; +            } + +            playSoundEffect(SoundEffectConstants.CLICK); +            showOverflowMenu(); +            return true; +        } + +        public boolean needsDividerBefore() { +            return false; +        } + +        public boolean needsDividerAfter() { +            return false; +        } + +        @Override +        protected void onAttachedToWindow() { +            super.onAttachedToWindow(); +            for (View_OnAttachStateChangeListener listener : mListeners) { +                listener.onViewAttachedToWindow(this); +            } +        } + +        @Override +        protected void onDetachedFromWindow() { +            super.onDetachedFromWindow(); +            for (View_OnAttachStateChangeListener listener : mListeners) { +                listener.onViewDetachedFromWindow(this); +            } + +            if (mOverflowPopup != null) mOverflowPopup.dismiss(); +        } + +        @Override +        public void addOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) { +            mListeners.add(listener); +        } + +        @Override +        public void removeOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) { +            mListeners.remove(listener); +        } +    } + +    private class OverflowPopup extends MenuPopupHelper { +        public OverflowPopup(Context context, MenuBuilder menu, View anchorView, +                boolean overflowOnly) { +            super(context, menu, anchorView, overflowOnly); +            setCallback(mPopupPresenterCallback); +        } + +        @Override +        public void onDismiss() { +            super.onDismiss(); +            mMenu.close(); +            mOverflowPopup = null; +        } +    } + +    private class ActionButtonSubmenu extends MenuPopupHelper { +        //UNUSED private SubMenuBuilder mSubMenu; + +        public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) { +            super(context, subMenu); +            //UNUSED mSubMenu = subMenu; + +            MenuItemImpl item = (MenuItemImpl) subMenu.getItem(); +            if (!item.isActionButton()) { +                // Give a reasonable anchor to nested submenus. +                setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton); +            } + +            setCallback(mPopupPresenterCallback); + +            boolean preserveIconSpacing = false; +            final int count = subMenu.size(); +            for (int i = 0; i < count; i++) { +                MenuItem childItem = subMenu.getItem(i); +                if (childItem.isVisible() && childItem.getIcon() != null) { +                    preserveIconSpacing = true; +                    break; +                } +            } +            setForceShowIcon(preserveIconSpacing); +        } + +        @Override +        public void onDismiss() { +            super.onDismiss(); +            mActionButtonPopup = null; +            mOpenSubMenuId = 0; +        } +    } + +    private class PopupPresenterCallback implements MenuPresenter.Callback { + +        @Override +        public boolean onOpenSubMenu(MenuBuilder subMenu) { +            if (subMenu == null) return false; + +            mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId(); +            return false; +        } + +        @Override +        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { +            if (menu instanceof SubMenuBuilder) { +                ((SubMenuBuilder) menu).getRootMenu().close(false); +            } +        } +    } + +    private class OpenOverflowRunnable implements Runnable { +        private OverflowPopup mPopup; + +        public OpenOverflowRunnable(OverflowPopup popup) { +            mPopup = popup; +        } + +        public void run() { +            mMenu.changeMenuMode(); +            final View menuView = (View) mMenuView; +            if (menuView != null && menuView.getWindowToken() != null && mPopup.tryShow()) { +                mOverflowPopup = mPopup; +            } +            mPostedOpenRunnable = null; +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java new file mode 100644 index 000000000..0e3b1ae0d --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.view.menu; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Canvas; +import android.os.Build; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; +import android.widget.LinearLayout; +import com.actionbarsherlock.internal.widget.IcsLinearLayout; + +/** + * @hide + */ +public class ActionMenuView extends IcsLinearLayout implements MenuBuilder.ItemInvoker, MenuView { +    //UNUSED private static final String TAG = "ActionMenuView"; +    private static final boolean IS_FROYO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO; + +    static final int MIN_CELL_SIZE = 56; // dips +    static final int GENERATED_ITEM_PADDING = 4; // dips + +    private MenuBuilder mMenu; + +    private boolean mReserveOverflow; +    private ActionMenuPresenter mPresenter; +    private boolean mFormatItems; +    private int mFormatItemsWidth; +    private int mMinCellSize; +    private int mGeneratedItemPadding; +    //UNUSED private int mMeasuredExtraWidth; + +    private boolean mFirst = true; + +    public ActionMenuView(Context context) { +        this(context, null); +    } + +    public ActionMenuView(Context context, AttributeSet attrs) { +        super(context, attrs); +        setBaselineAligned(false); +        final float density = context.getResources().getDisplayMetrics().density; +        mMinCellSize = (int) (MIN_CELL_SIZE * density); +        mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density); +    } + +    public void setPresenter(ActionMenuPresenter presenter) { +        mPresenter = presenter; +    } + +    public boolean isExpandedFormat() { +        return mFormatItems; +    } + +    @Override +    public void onConfigurationChanged(Configuration newConfig) { +        if (IS_FROYO) { +            super.onConfigurationChanged(newConfig); +        } +        mPresenter.updateMenuView(false); + +        if (mPresenter != null && mPresenter.isOverflowMenuShowing()) { +            mPresenter.hideOverflowMenu(); +            mPresenter.showOverflowMenu(); +        } +    } + +    @Override +    protected void onDraw(Canvas canvas) { +        //Need to trigger a relayout since we may have been added extremely +        //late in the initial rendering (e.g., when contained in a ViewPager). +        //See: https://github.com/JakeWharton/ActionBarSherlock/issues/272 +        if (!IS_FROYO && mFirst) { +            mFirst = false; +            requestLayout(); +            return; +        } +        super.onDraw(canvas); +    } + +    @Override +    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        // If we've been given an exact size to match, apply special formatting during layout. +        final boolean wasFormatted = mFormatItems; +        mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY; + +        if (wasFormatted != mFormatItems) { +            mFormatItemsWidth = 0; // Reset this when switching modes +        } + +        // Special formatting can change whether items can fit as action buttons. +        // Kick the menu and update presenters when this changes. +        final int widthSize = MeasureSpec.getMode(widthMeasureSpec); +        if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) { +            mFormatItemsWidth = widthSize; +            mMenu.onItemsChanged(true); +        } + +        if (mFormatItems) { +            onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec); +        } else { +            super.onMeasure(widthMeasureSpec, heightMeasureSpec); +        } +    } + +    private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) { +        // We already know the width mode is EXACTLY if we're here. +        final int heightMode = MeasureSpec.getMode(heightMeasureSpec); +        int widthSize = MeasureSpec.getSize(widthMeasureSpec); +        int heightSize = MeasureSpec.getSize(heightMeasureSpec); + +        final int widthPadding = getPaddingLeft() + getPaddingRight(); +        final int heightPadding = getPaddingTop() + getPaddingBottom(); + +        widthSize -= widthPadding; + +        // Divide the view into cells. +        final int cellCount = widthSize / mMinCellSize; +        final int cellSizeRemaining = widthSize % mMinCellSize; + +        if (cellCount == 0) { +            // Give up, nothing fits. +            setMeasuredDimension(widthSize, 0); +            return; +        } + +        final int cellSize = mMinCellSize + cellSizeRemaining / cellCount; + +        int cellsRemaining = cellCount; +        int maxChildHeight = 0; +        int maxCellsUsed = 0; +        int expandableItemCount = 0; +        int visibleItemCount = 0; +        boolean hasOverflow = false; + +        // This is used as a bitfield to locate the smallest items present. Assumes childCount < 64. +        long smallestItemsAt = 0; + +        final int childCount = getChildCount(); +        for (int i = 0; i < childCount; i++) { +            final View child = getChildAt(i); +            if (child.getVisibility() == GONE) continue; + +            final boolean isGeneratedItem = child instanceof ActionMenuItemView; +            visibleItemCount++; + +            if (isGeneratedItem) { +                // Reset padding for generated menu item views; it may change below +                // and views are recycled. +                child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0); +            } + +            final LayoutParams lp = (LayoutParams) child.getLayoutParams(); +            lp.expanded = false; +            lp.extraPixels = 0; +            lp.cellsUsed = 0; +            lp.expandable = false; +            lp.leftMargin = 0; +            lp.rightMargin = 0; +            lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText(); + +            // Overflow always gets 1 cell. No more, no less. +            final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining; + +            final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable, +                    heightMeasureSpec, heightPadding); + +            maxCellsUsed = Math.max(maxCellsUsed, cellsUsed); +            if (lp.expandable) expandableItemCount++; +            if (lp.isOverflowButton) hasOverflow = true; + +            cellsRemaining -= cellsUsed; +            maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight()); +            if (cellsUsed == 1) smallestItemsAt |= (1 << i); +        } + +        // When we have overflow and a single expanded (text) item, we want to try centering it +        // visually in the available space even though overflow consumes some of it. +        final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2; + +        // Divide space for remaining cells if we have items that can expand. +        // Try distributing whole leftover cells to smaller items first. + +        boolean needsExpansion = false; +        while (expandableItemCount > 0 && cellsRemaining > 0) { +            int minCells = Integer.MAX_VALUE; +            long minCellsAt = 0; // Bit locations are indices of relevant child views +            int minCellsItemCount = 0; +            for (int i = 0; i < childCount; i++) { +                final View child = getChildAt(i); +                final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + +                // Don't try to expand items that shouldn't. +                if (!lp.expandable) continue; + +                // Mark indices of children that can receive an extra cell. +                if (lp.cellsUsed < minCells) { +                    minCells = lp.cellsUsed; +                    minCellsAt = 1 << i; +                    minCellsItemCount = 1; +                } else if (lp.cellsUsed == minCells) { +                    minCellsAt |= 1 << i; +                    minCellsItemCount++; +                } +            } + +            // Items that get expanded will always be in the set of smallest items when we're done. +            smallestItemsAt |= minCellsAt; + +            if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop. + +            // We have enough cells, all minimum size items will be incremented. +            minCells++; + +            for (int i = 0; i < childCount; i++) { +                final View child = getChildAt(i); +                final LayoutParams lp = (LayoutParams) child.getLayoutParams(); +                if ((minCellsAt & (1 << i)) == 0) { +                    // If this item is already at our small item count, mark it for later. +                    if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i; +                    continue; +                } + +                if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) { +                    // Add padding to this item such that it centers. +                    child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0); +                } +                lp.cellsUsed++; +                lp.expanded = true; +                cellsRemaining--; +            } + +            needsExpansion = true; +        } + +        // Divide any space left that wouldn't divide along cell boundaries +        // evenly among the smallest items + +        final boolean singleItem = !hasOverflow && visibleItemCount == 1; +        if (cellsRemaining > 0 && smallestItemsAt != 0 && +                (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) { +            float expandCount = Long.bitCount(smallestItemsAt); + +            if (!singleItem) { +                // The items at the far edges may only expand by half in order to pin to either side. +                if ((smallestItemsAt & 1) != 0) { +                    LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams(); +                    if (!lp.preventEdgeOffset) expandCount -= 0.5f; +                } +                if ((smallestItemsAt & (1 << (childCount - 1))) != 0) { +                    LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams()); +                    if (!lp.preventEdgeOffset) expandCount -= 0.5f; +                } +            } + +            final int extraPixels = expandCount > 0 ? +                    (int) (cellsRemaining * cellSize / expandCount) : 0; + +            for (int i = 0; i < childCount; i++) { +                if ((smallestItemsAt & (1 << i)) == 0) continue; + +                final View child = getChildAt(i); +                final LayoutParams lp = (LayoutParams) child.getLayoutParams(); +                if (child instanceof ActionMenuItemView) { +                    // If this is one of our views, expand and measure at the larger size. +                    lp.extraPixels = extraPixels; +                    lp.expanded = true; +                    if (i == 0 && !lp.preventEdgeOffset) { +                        // First item gets part of its new padding pushed out of sight. +                        // The last item will get this implicitly from layout. +                        lp.leftMargin = -extraPixels / 2; +                    } +                    needsExpansion = true; +                } else if (lp.isOverflowButton) { +                    lp.extraPixels = extraPixels; +                    lp.expanded = true; +                    lp.rightMargin = -extraPixels / 2; +                    needsExpansion = true; +                } else { +                    // If we don't know what it is, give it some margins instead +                    // and let it center within its space. We still want to pin +                    // against the edges. +                    if (i != 0) { +                        lp.leftMargin = extraPixels / 2; +                    } +                    if (i != childCount - 1) { +                        lp.rightMargin = extraPixels / 2; +                    } +                } +            } + +            cellsRemaining = 0; +        } + +        // Remeasure any items that have had extra space allocated to them. +        if (needsExpansion) { +            int heightSpec = MeasureSpec.makeMeasureSpec(heightSize - heightPadding, heightMode); +            for (int i = 0; i < childCount; i++) { +                final View child = getChildAt(i); +                final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + +                if (!lp.expanded) continue; + +                final int width = lp.cellsUsed * cellSize + lp.extraPixels; +                child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), heightSpec); +            } +        } + +        if (heightMode != MeasureSpec.EXACTLY) { +            heightSize = maxChildHeight; +        } + +        setMeasuredDimension(widthSize, heightSize); +        //UNUSED mMeasuredExtraWidth = cellsRemaining * cellSize; +    } + +    /** +     * Measure a child view to fit within cell-based formatting. The child's width +     * will be measured to a whole multiple of cellSize. +     * +     * <p>Sets the expandable and cellsUsed fields of LayoutParams. +     * +     * @param child Child to measure +     * @param cellSize Size of one cell +     * @param cellsRemaining Number of cells remaining that this view can expand to fill +     * @param parentHeightMeasureSpec MeasureSpec used by the parent view +     * @param parentHeightPadding Padding present in the parent view +     * @return Number of cells this child was measured to occupy +     */ +    static int measureChildForCells(View child, int cellSize, int cellsRemaining, +            int parentHeightMeasureSpec, int parentHeightPadding) { +        final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + +        final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) - +                parentHeightPadding; +        final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec); +        final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode); + +        int cellsUsed = 0; +        if (cellsRemaining > 0) { +            final int childWidthSpec = MeasureSpec.makeMeasureSpec( +                    cellSize * cellsRemaining, MeasureSpec.AT_MOST); +            child.measure(childWidthSpec, childHeightSpec); + +            final int measuredWidth = child.getMeasuredWidth(); +            cellsUsed = measuredWidth / cellSize; +            if (measuredWidth % cellSize != 0) cellsUsed++; +        } + +        final ActionMenuItemView itemView = child instanceof ActionMenuItemView ? +                (ActionMenuItemView) child : null; +        final boolean expandable = !lp.isOverflowButton && itemView != null && itemView.hasText(); +        lp.expandable = expandable; + +        lp.cellsUsed = cellsUsed; +        final int targetWidth = cellsUsed * cellSize; +        child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY), +                childHeightSpec); +        return cellsUsed; +    } + +    @Override +    protected void onLayout(boolean changed, int left, int top, int right, int bottom) { +        if (!mFormatItems) { +            super.onLayout(changed, left, top, right, bottom); +            return; +        } + +        final int childCount = getChildCount(); +        final int midVertical = (top + bottom) / 2; +        final int dividerWidth = 0;//getDividerWidth(); +        int overflowWidth = 0; +        //UNUSED int nonOverflowWidth = 0; +        int nonOverflowCount = 0; +        int widthRemaining = right - left - getPaddingRight() - getPaddingLeft(); +        boolean hasOverflow = false; +        for (int i = 0; i < childCount; i++) { +            final View v = getChildAt(i); +            if (v.getVisibility() == GONE) { +                continue; +            } + +            LayoutParams p = (LayoutParams) v.getLayoutParams(); +            if (p.isOverflowButton) { +                overflowWidth = v.getMeasuredWidth(); +                if (hasDividerBeforeChildAt(i)) { +                    overflowWidth += dividerWidth; +                } + +                int height = v.getMeasuredHeight(); +                int r = getWidth() - getPaddingRight() - p.rightMargin; +                int l = r - overflowWidth; +                int t = midVertical - (height / 2); +                int b = t + height; +                v.layout(l, t, r, b); + +                widthRemaining -= overflowWidth; +                hasOverflow = true; +            } else { +                final int size = v.getMeasuredWidth() + p.leftMargin + p.rightMargin; +                //UNUSED nonOverflowWidth += size; +                widthRemaining -= size; +                //if (hasDividerBeforeChildAt(i)) { +                    //UNUSED nonOverflowWidth += dividerWidth; +                //} +                nonOverflowCount++; +            } +        } + +        if (childCount == 1 && !hasOverflow) { +            // Center a single child +            final View v = getChildAt(0); +            final int width = v.getMeasuredWidth(); +            final int height = v.getMeasuredHeight(); +            final int midHorizontal = (right - left) / 2; +            final int l = midHorizontal - width / 2; +            final int t = midVertical - height / 2; +            v.layout(l, t, l + width, t + height); +            return; +        } + +        final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1); +        final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0); + +        int startLeft = getPaddingLeft(); +        for (int i = 0; i < childCount; i++) { +            final View v = getChildAt(i); +            final LayoutParams lp = (LayoutParams) v.getLayoutParams(); +            if (v.getVisibility() == GONE || lp.isOverflowButton) { +                continue; +            } + +            startLeft += lp.leftMargin; +            int width = v.getMeasuredWidth(); +            int height = v.getMeasuredHeight(); +            int t = midVertical - height / 2; +            v.layout(startLeft, t, startLeft + width, t + height); +            startLeft += width + lp.rightMargin + spacerSize; +        } +    } + +    @Override +    public void onDetachedFromWindow() { +        super.onDetachedFromWindow(); +        mPresenter.dismissPopupMenus(); +    } + +    public boolean isOverflowReserved() { +        return mReserveOverflow; +    } + +    public void setOverflowReserved(boolean reserveOverflow) { +        mReserveOverflow = reserveOverflow; +    } + +    @Override +    protected LayoutParams generateDefaultLayoutParams() { +        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, +                LayoutParams.WRAP_CONTENT); +        params.gravity = Gravity.CENTER_VERTICAL; +        return params; +    } + +    @Override +    public LayoutParams generateLayoutParams(AttributeSet attrs) { +        return new LayoutParams(getContext(), attrs); +    } + +    @Override +    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { +        if (p instanceof LayoutParams) { +            LayoutParams result = new LayoutParams((LayoutParams) p); +            if (result.gravity <= Gravity.NO_GRAVITY) { +                result.gravity = Gravity.CENTER_VERTICAL; +            } +            return result; +        } +        return generateDefaultLayoutParams(); +    } + +    @Override +    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { +        return p != null && p instanceof LayoutParams; +    } + +    public LayoutParams generateOverflowButtonLayoutParams() { +        LayoutParams result = generateDefaultLayoutParams(); +        result.isOverflowButton = true; +        return result; +    } + +    public boolean invokeItem(MenuItemImpl item) { +        return mMenu.performItemAction(item, 0); +    } + +    public int getWindowAnimations() { +        return 0; +    } + +    public void initialize(MenuBuilder menu) { +        mMenu = menu; +    } + +    //@Override +    protected boolean hasDividerBeforeChildAt(int childIndex) { +        if (childIndex == 0) { +            return false; +        } +        final View childBefore = getChildAt(childIndex - 1); +        final View child = getChildAt(childIndex); +        boolean result = false; +        if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) { +            result |= ((ActionMenuChildView) childBefore).needsDividerAfter(); +        } +        if (childIndex > 0 && child instanceof ActionMenuChildView) { +            result |= ((ActionMenuChildView) child).needsDividerBefore(); +        } +        return result; +    } + +    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { +        return false; +    } + +    public interface ActionMenuChildView { +        public boolean needsDividerBefore(); +        public boolean needsDividerAfter(); +    } + +    public static class LayoutParams extends LinearLayout.LayoutParams { +        public boolean isOverflowButton; +        public int cellsUsed; +        public int extraPixels; +        public boolean expandable; +        public boolean preventEdgeOffset; + +        public boolean expanded; + +        public LayoutParams(Context c, AttributeSet attrs) { +            super(c, attrs); +        } + +        public LayoutParams(LayoutParams other) { +            super((LinearLayout.LayoutParams) other); +            isOverflowButton = other.isOverflowButton; +        } + +        public LayoutParams(int width, int height) { +            super(width, height); +            isOverflowButton = false; +        } + +        public LayoutParams(int width, int height, boolean isOverflowButton) { +            super(width, height); +            this.isOverflowButton = isOverflowButton; +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java new file mode 100644 index 000000000..6da26f2ae --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.actionbarsherlock.internal.view.menu; + +import java.util.ArrayList; +import android.content.Context; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +/** + * Base class for MenuPresenters that have a consistent container view and item + * views. Behaves similarly to an AdapterView in that existing item views will + * be reused if possible when items change. + */ +public abstract class BaseMenuPresenter implements MenuPresenter { +    private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; + +    protected Context mSystemContext; +    protected Context mContext; +    protected MenuBuilder mMenu; +    protected LayoutInflater mSystemInflater; +    protected LayoutInflater mInflater; +    private Callback mCallback; + +    private int mMenuLayoutRes; +    private int mItemLayoutRes; + +    protected MenuView mMenuView; + +    private int mId; + +    /** +     * Construct a new BaseMenuPresenter. +     * +     * @param context Context for generating system-supplied views +     * @param menuLayoutRes Layout resource ID for the menu container view +     * @param itemLayoutRes Layout resource ID for a single item view +     */ +    public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) { +        mSystemContext = context; +        mSystemInflater = LayoutInflater.from(context); +        mMenuLayoutRes = menuLayoutRes; +        mItemLayoutRes = itemLayoutRes; +    } + +    @Override +    public void initForMenu(Context context, MenuBuilder menu) { +        mContext = context; +        mInflater = LayoutInflater.from(mContext); +        mMenu = menu; +    } + +    @Override +    public MenuView getMenuView(ViewGroup root) { +        if (mMenuView == null) { +            mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false); +            mMenuView.initialize(mMenu); +            updateMenuView(true); +        } + +        return mMenuView; +    } + +    /** +     * Reuses item views when it can +     */ +    public void updateMenuView(boolean cleared) { +        final ViewGroup parent = (ViewGroup) mMenuView; +        if (parent == null) return; + +        int childIndex = 0; +        if (mMenu != null) { +            mMenu.flagActionItems(); +            ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems(); +            final int itemCount = visibleItems.size(); +            for (int i = 0; i < itemCount; i++) { +                MenuItemImpl item = visibleItems.get(i); +                if (shouldIncludeItem(childIndex, item)) { +                    final View convertView = parent.getChildAt(childIndex); +                    final MenuItemImpl oldItem = convertView instanceof MenuView.ItemView ? +                            ((MenuView.ItemView) convertView).getItemData() : null; +                    final View itemView = getItemView(item, convertView, parent); +                    if (item != oldItem) { +                        // Don't let old states linger with new data. +                        itemView.setPressed(false); +                        if (IS_HONEYCOMB) itemView.jumpDrawablesToCurrentState(); +                    } +                    if (itemView != convertView) { +                        addItemView(itemView, childIndex); +                    } +                    childIndex++; +                } +            } +        } + +        // Remove leftover views. +        while (childIndex < parent.getChildCount()) { +            if (!filterLeftoverView(parent, childIndex)) { +                childIndex++; +            } +        } +    } + +    /** +     * Add an item view at the given index. +     * +     * @param itemView View to add +     * @param childIndex Index within the parent to insert at +     */ +    protected void addItemView(View itemView, int childIndex) { +        final ViewGroup currentParent = (ViewGroup) itemView.getParent(); +        if (currentParent != null) { +            currentParent.removeView(itemView); +        } +        ((ViewGroup) mMenuView).addView(itemView, childIndex); +    } + +    /** +     * Filter the child view at index and remove it if appropriate. +     * @param parent Parent to filter from +     * @param childIndex Index to filter +     * @return true if the child view at index was removed +     */ +    protected boolean filterLeftoverView(ViewGroup parent, int childIndex) { +        parent.removeViewAt(childIndex); +        return true; +    } + +    public void setCallback(Callback cb) { +        mCallback = cb; +    } + +    /** +     * Create a new item view that can be re-bound to other item data later. +     * +     * @return The new item view +     */ +    public MenuView.ItemView createItemView(ViewGroup parent) { +        return (MenuView.ItemView) mSystemInflater.inflate(mItemLayoutRes, parent, false); +    } + +    /** +     * Prepare an item view for use. See AdapterView for the basic idea at work here. +     * This may require creating a new item view, but well-behaved implementations will +     * re-use the view passed as convertView if present. The returned view will be populated +     * with data from the item parameter. +     * +     * @param item Item to present +     * @param convertView Existing view to reuse +     * @param parent Intended parent view - use for inflation. +     * @return View that presents the requested menu item +     */ +    public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) { +        MenuView.ItemView itemView; +        if (convertView instanceof MenuView.ItemView) { +            itemView = (MenuView.ItemView) convertView; +        } else { +            itemView = createItemView(parent); +        } +        bindItemView(item, itemView); +        return (View) itemView; +    } + +    /** +     * Bind item data to an existing item view. +     * +     * @param item Item to bind +     * @param itemView View to populate with item data +     */ +    public abstract void bindItemView(MenuItemImpl item, MenuView.ItemView itemView); + +    /** +     * Filter item by child index and item data. +     * +     * @param childIndex Indended presentation index of this item +     * @param item Item to present +     * @return true if this item should be included in this menu presentation; false otherwise +     */ +    public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) { +        return true; +    } + +    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { +        if (mCallback != null) { +            mCallback.onCloseMenu(menu, allMenusAreClosing); +        } +    } + +    public boolean onSubMenuSelected(SubMenuBuilder menu) { +        if (mCallback != null) { +            return mCallback.onOpenSubMenu(menu); +        } +        return false; +    } + +    public boolean flagActionItems() { +        return false; +    } + +    public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { +        return false; +    } + +    public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { +        return false; +    } + +    public int getId() { +        return mId; +    } + +    public void setId(int id) { +        mId = id; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java new file mode 100644 index 000000000..ac25c3736 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2006 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.actionbarsherlock.internal.view.menu; + +import com.actionbarsherlock.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RadioButton; +import android.widget.TextView; + +/** + * The item view for each item in the ListView-based MenuViews. + */ +public class ListMenuItemView extends LinearLayout implements MenuView.ItemView { +    private MenuItemImpl mItemData; + +    private ImageView mIconView; +    private RadioButton mRadioButton; +    private TextView mTitleView; +    private CheckBox mCheckBox; +    private TextView mShortcutView; + +    private Drawable mBackground; +    private int mTextAppearance; +    private Context mTextAppearanceContext; +    private boolean mPreserveIconSpacing; + +    //UNUSED private int mMenuType; + +    private LayoutInflater mInflater; + +    private boolean mForceShowIcon; + +    final Context mContext; + +    public ListMenuItemView(Context context, AttributeSet attrs, int defStyle) { +        super(context, attrs); +        mContext = context; + +        TypedArray a = +            context.obtainStyledAttributes( +                attrs, R.styleable.SherlockMenuView, defStyle, 0); + +        mBackground = a.getDrawable(R.styleable.SherlockMenuView_itemBackground); +        mTextAppearance = a.getResourceId(R.styleable. +                                          SherlockMenuView_itemTextAppearance, -1); +        mPreserveIconSpacing = a.getBoolean( +                R.styleable.SherlockMenuView_preserveIconSpacing, false); +        mTextAppearanceContext = context; + +        a.recycle(); +    } + +    public ListMenuItemView(Context context, AttributeSet attrs) { +        this(context, attrs, 0); +    } + +    @Override +    protected void onFinishInflate() { +        super.onFinishInflate(); + +        setBackgroundDrawable(mBackground); + +        mTitleView = (TextView) findViewById(R.id.abs__title); +        if (mTextAppearance != -1) { +            mTitleView.setTextAppearance(mTextAppearanceContext, +                                         mTextAppearance); +        } + +        mShortcutView = (TextView) findViewById(R.id.abs__shortcut); +    } + +    public void initialize(MenuItemImpl itemData, int menuType) { +        mItemData = itemData; +        //UNUSED mMenuType = menuType; + +        setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE); + +        setTitle(itemData.getTitleForItemView(this)); +        setCheckable(itemData.isCheckable()); +        setShortcut(itemData.shouldShowShortcut(), itemData.getShortcut()); +        setIcon(itemData.getIcon()); +        setEnabled(itemData.isEnabled()); +    } + +    public void setForceShowIcon(boolean forceShow) { +        mPreserveIconSpacing = mForceShowIcon = forceShow; +    } + +    public void setTitle(CharSequence title) { +        if (title != null) { +            mTitleView.setText(title); + +            if (mTitleView.getVisibility() != VISIBLE) mTitleView.setVisibility(VISIBLE); +        } else { +            if (mTitleView.getVisibility() != GONE) mTitleView.setVisibility(GONE); +        } +    } + +    public MenuItemImpl getItemData() { +        return mItemData; +    } + +    public void setCheckable(boolean checkable) { + +        if (!checkable && mRadioButton == null && mCheckBox == null) { +            return; +        } + +        if (mRadioButton == null) { +            insertRadioButton(); +        } +        if (mCheckBox == null) { +            insertCheckBox(); +        } + +        // Depending on whether its exclusive check or not, the checkbox or +        // radio button will be the one in use (and the other will be otherCompoundButton) +        final CompoundButton compoundButton; +        final CompoundButton otherCompoundButton; + +        if (mItemData.isExclusiveCheckable()) { +            compoundButton = mRadioButton; +            otherCompoundButton = mCheckBox; +        } else { +            compoundButton = mCheckBox; +            otherCompoundButton = mRadioButton; +        } + +        if (checkable) { +            compoundButton.setChecked(mItemData.isChecked()); + +            final int newVisibility = checkable ? VISIBLE : GONE; +            if (compoundButton.getVisibility() != newVisibility) { +                compoundButton.setVisibility(newVisibility); +            } + +            // Make sure the other compound button isn't visible +            if (otherCompoundButton.getVisibility() != GONE) { +                otherCompoundButton.setVisibility(GONE); +            } +        } else { +            mCheckBox.setVisibility(GONE); +            mRadioButton.setVisibility(GONE); +        } +    } + +    public void setChecked(boolean checked) { +        CompoundButton compoundButton; + +        if (mItemData.isExclusiveCheckable()) { +            if (mRadioButton == null) { +                insertRadioButton(); +            } +            compoundButton = mRadioButton; +        } else { +            if (mCheckBox == null) { +                insertCheckBox(); +            } +            compoundButton = mCheckBox; +        } + +        compoundButton.setChecked(checked); +    } + +    public void setShortcut(boolean showShortcut, char shortcutKey) { +        final int newVisibility = (showShortcut && mItemData.shouldShowShortcut()) +                ? VISIBLE : GONE; + +        if (newVisibility == VISIBLE) { +            mShortcutView.setText(mItemData.getShortcutLabel()); +        } + +        if (mShortcutView.getVisibility() != newVisibility) { +            mShortcutView.setVisibility(newVisibility); +        } +    } + +    public void setIcon(Drawable icon) { +        final boolean showIcon = mItemData.shouldShowIcon() || mForceShowIcon; +        if (!showIcon && !mPreserveIconSpacing) { +            return; +        } + +        if (mIconView == null && icon == null && !mPreserveIconSpacing) { +            return; +        } + +        if (mIconView == null) { +            insertIconView(); +        } + +        if (icon != null || mPreserveIconSpacing) { +            mIconView.setImageDrawable(showIcon ? icon : null); + +            if (mIconView.getVisibility() != VISIBLE) { +                mIconView.setVisibility(VISIBLE); +            } +        } else { +            mIconView.setVisibility(GONE); +        } +    } + +    @Override +    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        if (mIconView != null && mPreserveIconSpacing) { +            // Enforce minimum icon spacing +            ViewGroup.LayoutParams lp = getLayoutParams(); +            LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); +            if (lp.height > 0 && iconLp.width <= 0) { +                iconLp.width = lp.height; +            } +        } +        super.onMeasure(widthMeasureSpec, heightMeasureSpec); +    } + +    private void insertIconView() { +        LayoutInflater inflater = getInflater(); +        mIconView = (ImageView) inflater.inflate(R.layout.abs__list_menu_item_icon, +                this, false); +        addView(mIconView, 0); +    } + +    private void insertRadioButton() { +        LayoutInflater inflater = getInflater(); +        mRadioButton = +                (RadioButton) inflater.inflate(R.layout.abs__list_menu_item_radio, +                this, false); +        addView(mRadioButton); +    } + +    private void insertCheckBox() { +        LayoutInflater inflater = getInflater(); +        mCheckBox = +                (CheckBox) inflater.inflate(R.layout.abs__list_menu_item_checkbox, +                this, false); +        addView(mCheckBox); +    } + +    public boolean prefersCondensedTitle() { +        return false; +    } + +    public boolean showsIcon() { +        return mForceShowIcon; +    } + +    private LayoutInflater getInflater() { +        if (mInflater == null) { +            mInflater = LayoutInflater.from(mContext); +        } +        return mInflater; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java new file mode 100644 index 000000000..179b8f037 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java @@ -0,0 +1,1335 @@ +/* + * Copyright (C) 2006 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.actionbarsherlock.internal.view.menu; + + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.SparseArray; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.View; + +import com.actionbarsherlock.R; +import com.actionbarsherlock.view.ActionProvider; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.SubMenu; + +/** + * Implementation of the {@link android.view.Menu} interface for creating a + * standard menu UI. + */ +public class MenuBuilder implements Menu { +    //UNUSED private static final String TAG = "MenuBuilder"; + +    private static final String PRESENTER_KEY = "android:menu:presenters"; +    private static final String ACTION_VIEW_STATES_KEY = "android:menu:actionviewstates"; +    private static final String EXPANDED_ACTION_VIEW_ID = "android:menu:expandedactionview"; + +    private static final int[]  sCategoryToOrder = new int[] { +        1, /* No category */ +        4, /* CONTAINER */ +        5, /* SYSTEM */ +        3, /* SECONDARY */ +        2, /* ALTERNATIVE */ +        0, /* SELECTED_ALTERNATIVE */ +    }; + +    private final Context mContext; +    private final Resources mResources; + +    /** +     * Whether the shortcuts should be qwerty-accessible. Use isQwertyMode() +     * instead of accessing this directly. +     */ +    private boolean mQwertyMode; + +    /** +     * Whether the shortcuts should be visible on menus. Use isShortcutsVisible() +     * instead of accessing this directly. +     */ +    private boolean mShortcutsVisible; + +    /** +     * Callback that will receive the various menu-related events generated by +     * this class. Use getCallback to get a reference to the callback. +     */ +    private Callback mCallback; + +    /** Contains all of the items for this menu */ +    private ArrayList<MenuItemImpl> mItems; + +    /** Contains only the items that are currently visible.  This will be created/refreshed from +     * {@link #getVisibleItems()} */ +    private ArrayList<MenuItemImpl> mVisibleItems; +    /** +     * Whether or not the items (or any one item's shown state) has changed since it was last +     * fetched from {@link #getVisibleItems()} +     */ +    private boolean mIsVisibleItemsStale; + +    /** +     * Contains only the items that should appear in the Action Bar, if present. +     */ +    private ArrayList<MenuItemImpl> mActionItems; +    /** +     * Contains items that should NOT appear in the Action Bar, if present. +     */ +    private ArrayList<MenuItemImpl> mNonActionItems; + +    /** +     * Whether or not the items (or any one item's action state) has changed since it was +     * last fetched. +     */ +    private boolean mIsActionItemsStale; + +    /** +     * Default value for how added items should show in the action list. +     */ +    private int mDefaultShowAsAction = MenuItem.SHOW_AS_ACTION_NEVER; + +    /** +     * Current use case is Context Menus: As Views populate the context menu, each one has +     * extra information that should be passed along.  This is the current menu info that +     * should be set on all items added to this menu. +     */ +    private ContextMenuInfo mCurrentMenuInfo; + +    /** Header title for menu types that have a header (context and submenus) */ +    CharSequence mHeaderTitle; +    /** Header icon for menu types that have a header and support icons (context) */ +    Drawable mHeaderIcon; +    /** Header custom view for menu types that have a header and support custom views (context) */ +    View mHeaderView; + +    /** +     * Contains the state of the View hierarchy for all menu views when the menu +     * was frozen. +     */ +    //UNUSED private SparseArray<Parcelable> mFrozenViewStates; + +    /** +     * Prevents onItemsChanged from doing its junk, useful for batching commands +     * that may individually call onItemsChanged. +     */ +    private boolean mPreventDispatchingItemsChanged = false; +    private boolean mItemsChangedWhileDispatchPrevented = false; + +    private boolean mOptionalIconsVisible = false; + +    private boolean mIsClosing = false; + +    private ArrayList<MenuItemImpl> mTempShortcutItemList = new ArrayList<MenuItemImpl>(); + +    private CopyOnWriteArrayList<WeakReference<MenuPresenter>> mPresenters = +            new CopyOnWriteArrayList<WeakReference<MenuPresenter>>(); + +    /** +     * Currently expanded menu item; must be collapsed when we clear. +     */ +    private MenuItemImpl mExpandedItem; + +    /** +     * Called by menu to notify of close and selection changes. +     */ +    public interface Callback { +        /** +         * Called when a menu item is selected. +         * @param menu The menu that is the parent of the item +         * @param item The menu item that is selected +         * @return whether the menu item selection was handled +         */ +        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item); + +        /** +         * Called when the mode of the menu changes (for example, from icon to expanded). +         * +         * @param menu the menu that has changed modes +         */ +        public void onMenuModeChange(MenuBuilder menu); +    } + +    /** +     * Called by menu items to execute their associated action +     */ +    public interface ItemInvoker { +        public boolean invokeItem(MenuItemImpl item); +    } + +    public MenuBuilder(Context context) { +        mContext = context; +        mResources = context.getResources(); + +        mItems = new ArrayList<MenuItemImpl>(); + +        mVisibleItems = new ArrayList<MenuItemImpl>(); +        mIsVisibleItemsStale = true; + +        mActionItems = new ArrayList<MenuItemImpl>(); +        mNonActionItems = new ArrayList<MenuItemImpl>(); +        mIsActionItemsStale = true; + +        setShortcutsVisibleInner(true); +    } + +    public MenuBuilder setDefaultShowAsAction(int defaultShowAsAction) { +        mDefaultShowAsAction = defaultShowAsAction; +        return this; +    } + +    /** +     * Add a presenter to this menu. This will only hold a WeakReference; +     * you do not need to explicitly remove a presenter, but you can using +     * {@link #removeMenuPresenter(MenuPresenter)}. +     * +     * @param presenter The presenter to add +     */ +    public void addMenuPresenter(MenuPresenter presenter) { +        mPresenters.add(new WeakReference<MenuPresenter>(presenter)); +        presenter.initForMenu(mContext, this); +        mIsActionItemsStale = true; +    } + +    /** +     * Remove a presenter from this menu. That presenter will no longer +     * receive notifications of updates to this menu's data. +     * +     * @param presenter The presenter to remove +     */ +    public void removeMenuPresenter(MenuPresenter presenter) { +        for (WeakReference<MenuPresenter> ref : mPresenters) { +            final MenuPresenter item = ref.get(); +            if (item == null || item == presenter) { +                mPresenters.remove(ref); +            } +        } +    } + +    private void dispatchPresenterUpdate(boolean cleared) { +        if (mPresenters.isEmpty()) return; + +        stopDispatchingItemsChanged(); +        for (WeakReference<MenuPresenter> ref : mPresenters) { +            final MenuPresenter presenter = ref.get(); +            if (presenter == null) { +                mPresenters.remove(ref); +            } else { +                presenter.updateMenuView(cleared); +            } +        } +        startDispatchingItemsChanged(); +    } + +    private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu) { +        if (mPresenters.isEmpty()) return false; + +        boolean result = false; + +        for (WeakReference<MenuPresenter> ref : mPresenters) { +            final MenuPresenter presenter = ref.get(); +            if (presenter == null) { +                mPresenters.remove(ref); +            } else if (!result) { +                result = presenter.onSubMenuSelected(subMenu); +            } +        } +        return result; +    } + +    private void dispatchSaveInstanceState(Bundle outState) { +        if (mPresenters.isEmpty()) return; + +        SparseArray<Parcelable> presenterStates = new SparseArray<Parcelable>(); + +        for (WeakReference<MenuPresenter> ref : mPresenters) { +            final MenuPresenter presenter = ref.get(); +            if (presenter == null) { +                mPresenters.remove(ref); +            } else { +                final int id = presenter.getId(); +                if (id > 0) { +                    final Parcelable state = presenter.onSaveInstanceState(); +                    if (state != null) { +                        presenterStates.put(id, state); +                    } +                } +            } +        } + +        outState.putSparseParcelableArray(PRESENTER_KEY, presenterStates); +    } + +    private void dispatchRestoreInstanceState(Bundle state) { +        SparseArray<Parcelable> presenterStates = state.getSparseParcelableArray(PRESENTER_KEY); + +        if (presenterStates == null || mPresenters.isEmpty()) return; + +        for (WeakReference<MenuPresenter> ref : mPresenters) { +            final MenuPresenter presenter = ref.get(); +            if (presenter == null) { +                mPresenters.remove(ref); +            } else { +                final int id = presenter.getId(); +                if (id > 0) { +                    Parcelable parcel = presenterStates.get(id); +                    if (parcel != null) { +                        presenter.onRestoreInstanceState(parcel); +                    } +                } +            } +        } +    } + +    public void savePresenterStates(Bundle outState) { +        dispatchSaveInstanceState(outState); +    } + +    public void restorePresenterStates(Bundle state) { +        dispatchRestoreInstanceState(state); +    } + +    public void saveActionViewStates(Bundle outStates) { +        SparseArray<Parcelable> viewStates = null; + +        final int itemCount = size(); +        for (int i = 0; i < itemCount; i++) { +            final MenuItem item = getItem(i); +            final View v = item.getActionView(); +            if (v != null && v.getId() != View.NO_ID) { +                if (viewStates == null) { +                    viewStates = new SparseArray<Parcelable>(); +                } +                v.saveHierarchyState(viewStates); +                if (item.isActionViewExpanded()) { +                    outStates.putInt(EXPANDED_ACTION_VIEW_ID, item.getItemId()); +                } +            } +            if (item.hasSubMenu()) { +                final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu(); +                subMenu.saveActionViewStates(outStates); +            } +        } + +        if (viewStates != null) { +            outStates.putSparseParcelableArray(getActionViewStatesKey(), viewStates); +        } +    } + +    public void restoreActionViewStates(Bundle states) { +        if (states == null) { +            return; +        } + +        SparseArray<Parcelable> viewStates = states.getSparseParcelableArray( +                getActionViewStatesKey()); + +        final int itemCount = size(); +        for (int i = 0; i < itemCount; i++) { +            final MenuItem item = getItem(i); +            final View v = item.getActionView(); +            if (v != null && v.getId() != View.NO_ID) { +                v.restoreHierarchyState(viewStates); +            } +            if (item.hasSubMenu()) { +                final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu(); +                subMenu.restoreActionViewStates(states); +            } +        } + +        final int expandedId = states.getInt(EXPANDED_ACTION_VIEW_ID); +        if (expandedId > 0) { +            MenuItem itemToExpand = findItem(expandedId); +            if (itemToExpand != null) { +                itemToExpand.expandActionView(); +            } +        } +    } + +    protected String getActionViewStatesKey() { +        return ACTION_VIEW_STATES_KEY; +    } + +    public void setCallback(Callback cb) { +        mCallback = cb; +    } + +    /** +     * Adds an item to the menu.  The other add methods funnel to this. +     */ +    private MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) { +        final int ordering = getOrdering(categoryOrder); + +        final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder, +                ordering, title, mDefaultShowAsAction); + +        if (mCurrentMenuInfo != null) { +            // Pass along the current menu info +            item.setMenuInfo(mCurrentMenuInfo); +        } + +        mItems.add(findInsertIndex(mItems, ordering), item); +        onItemsChanged(true); + +        return item; +    } + +    public MenuItem add(CharSequence title) { +        return addInternal(0, 0, 0, title); +    } + +    public MenuItem add(int titleRes) { +        return addInternal(0, 0, 0, mResources.getString(titleRes)); +    } + +    public MenuItem add(int group, int id, int categoryOrder, CharSequence title) { +        return addInternal(group, id, categoryOrder, title); +    } + +    public MenuItem add(int group, int id, int categoryOrder, int title) { +        return addInternal(group, id, categoryOrder, mResources.getString(title)); +    } + +    public SubMenu addSubMenu(CharSequence title) { +        return addSubMenu(0, 0, 0, title); +    } + +    public SubMenu addSubMenu(int titleRes) { +        return addSubMenu(0, 0, 0, mResources.getString(titleRes)); +    } + +    public SubMenu addSubMenu(int group, int id, int categoryOrder, CharSequence title) { +        final MenuItemImpl item = (MenuItemImpl) addInternal(group, id, categoryOrder, title); +        final SubMenuBuilder subMenu = new SubMenuBuilder(mContext, this, item); +        item.setSubMenu(subMenu); + +        return subMenu; +    } + +    public SubMenu addSubMenu(int group, int id, int categoryOrder, int title) { +        return addSubMenu(group, id, categoryOrder, mResources.getString(title)); +    } + +    public int addIntentOptions(int group, int id, int categoryOrder, ComponentName caller, +            Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) { +        PackageManager pm = mContext.getPackageManager(); +        final List<ResolveInfo> lri = +                pm.queryIntentActivityOptions(caller, specifics, intent, 0); +        final int N = lri != null ? lri.size() : 0; + +        if ((flags & FLAG_APPEND_TO_GROUP) == 0) { +            removeGroup(group); +        } + +        for (int i=0; i<N; i++) { +            final ResolveInfo ri = lri.get(i); +            Intent rintent = new Intent( +                ri.specificIndex < 0 ? intent : specifics[ri.specificIndex]); +            rintent.setComponent(new ComponentName( +                    ri.activityInfo.applicationInfo.packageName, +                    ri.activityInfo.name)); +            final MenuItem item = add(group, id, categoryOrder, ri.loadLabel(pm)) +                    .setIcon(ri.loadIcon(pm)) +                    .setIntent(rintent); +            if (outSpecificItems != null && ri.specificIndex >= 0) { +                outSpecificItems[ri.specificIndex] = item; +            } +        } + +        return N; +    } + +    public void removeItem(int id) { +        removeItemAtInt(findItemIndex(id), true); +    } + +    public void removeGroup(int group) { +        final int i = findGroupIndex(group); + +        if (i >= 0) { +            final int maxRemovable = mItems.size() - i; +            int numRemoved = 0; +            while ((numRemoved++ < maxRemovable) && (mItems.get(i).getGroupId() == group)) { +                // Don't force update for each one, this method will do it at the end +                removeItemAtInt(i, false); +            } + +            // Notify menu views +            onItemsChanged(true); +        } +    } + +    /** +     * Remove the item at the given index and optionally forces menu views to +     * update. +     * +     * @param index The index of the item to be removed. If this index is +     *            invalid an exception is thrown. +     * @param updateChildrenOnMenuViews Whether to force update on menu views. +     *            Please make sure you eventually call this after your batch of +     *            removals. +     */ +    private void removeItemAtInt(int index, boolean updateChildrenOnMenuViews) { +        if ((index < 0) || (index >= mItems.size())) return; + +        mItems.remove(index); + +        if (updateChildrenOnMenuViews) onItemsChanged(true); +    } + +    public void removeItemAt(int index) { +        removeItemAtInt(index, true); +    } + +    public void clearAll() { +        mPreventDispatchingItemsChanged = true; +        clear(); +        clearHeader(); +        mPreventDispatchingItemsChanged = false; +        mItemsChangedWhileDispatchPrevented = false; +        onItemsChanged(true); +    } + +    public void clear() { +        if (mExpandedItem != null) { +            collapseItemActionView(mExpandedItem); +        } +        mItems.clear(); + +        onItemsChanged(true); +    } + +    void setExclusiveItemChecked(MenuItem item) { +        final int group = item.getGroupId(); + +        final int N = mItems.size(); +        for (int i = 0; i < N; i++) { +            MenuItemImpl curItem = mItems.get(i); +            if (curItem.getGroupId() == group) { +                if (!curItem.isExclusiveCheckable()) continue; +                if (!curItem.isCheckable()) continue; + +                // Check the item meant to be checked, uncheck the others (that are in the group) +                curItem.setCheckedInt(curItem == item); +            } +        } +    } + +    public void setGroupCheckable(int group, boolean checkable, boolean exclusive) { +        final int N = mItems.size(); + +        for (int i = 0; i < N; i++) { +            MenuItemImpl item = mItems.get(i); +            if (item.getGroupId() == group) { +                item.setExclusiveCheckable(exclusive); +                item.setCheckable(checkable); +            } +        } +    } + +    public void setGroupVisible(int group, boolean visible) { +        final int N = mItems.size(); + +        // We handle the notification of items being changed ourselves, so we use setVisibleInt rather +        // than setVisible and at the end notify of items being changed + +        boolean changedAtLeastOneItem = false; +        for (int i = 0; i < N; i++) { +            MenuItemImpl item = mItems.get(i); +            if (item.getGroupId() == group) { +                if (item.setVisibleInt(visible)) changedAtLeastOneItem = true; +            } +        } + +        if (changedAtLeastOneItem) onItemsChanged(true); +    } + +    public void setGroupEnabled(int group, boolean enabled) { +        final int N = mItems.size(); + +        for (int i = 0; i < N; i++) { +            MenuItemImpl item = mItems.get(i); +            if (item.getGroupId() == group) { +                item.setEnabled(enabled); +            } +        } +    } + +    public boolean hasVisibleItems() { +        final int size = size(); + +        for (int i = 0; i < size; i++) { +            MenuItemImpl item = mItems.get(i); +            if (item.isVisible()) { +                return true; +            } +        } + +        return false; +    } + +    public MenuItem findItem(int id) { +        final int size = size(); +        for (int i = 0; i < size; i++) { +            MenuItemImpl item = mItems.get(i); +            if (item.getItemId() == id) { +                return item; +            } else if (item.hasSubMenu()) { +                MenuItem possibleItem = item.getSubMenu().findItem(id); + +                if (possibleItem != null) { +                    return possibleItem; +                } +            } +        } + +        return null; +    } + +    public int findItemIndex(int id) { +        final int size = size(); + +        for (int i = 0; i < size; i++) { +            MenuItemImpl item = mItems.get(i); +            if (item.getItemId() == id) { +                return i; +            } +        } + +        return -1; +    } + +    public int findGroupIndex(int group) { +        return findGroupIndex(group, 0); +    } + +    public int findGroupIndex(int group, int start) { +        final int size = size(); + +        if (start < 0) { +            start = 0; +        } + +        for (int i = start; i < size; i++) { +            final MenuItemImpl item = mItems.get(i); + +            if (item.getGroupId() == group) { +                return i; +            } +        } + +        return -1; +    } + +    public int size() { +        return mItems.size(); +    } + +    /** {@inheritDoc} */ +    public MenuItem getItem(int index) { +        return mItems.get(index); +    } + +    public boolean isShortcutKey(int keyCode, KeyEvent event) { +        return findItemWithShortcutForKey(keyCode, event) != null; +    } + +    public void setQwertyMode(boolean isQwerty) { +        mQwertyMode = isQwerty; + +        onItemsChanged(false); +    } + +    /** +     * Returns the ordering across all items. This will grab the category from +     * the upper bits, find out how to order the category with respect to other +     * categories, and combine it with the lower bits. +     * +     * @param categoryOrder The category order for a particular item (if it has +     *            not been or/add with a category, the default category is +     *            assumed). +     * @return An ordering integer that can be used to order this item across +     *         all the items (even from other categories). +     */ +    private static int getOrdering(int categoryOrder) { +        final int index = (categoryOrder & CATEGORY_MASK) >> CATEGORY_SHIFT; + +        if (index < 0 || index >= sCategoryToOrder.length) { +            throw new IllegalArgumentException("order does not contain a valid category."); +        } + +        return (sCategoryToOrder[index] << CATEGORY_SHIFT) | (categoryOrder & USER_MASK); +    } + +    /** +     * @return whether the menu shortcuts are in qwerty mode or not +     */ +    boolean isQwertyMode() { +        return mQwertyMode; +    } + +    /** +     * Sets whether the shortcuts should be visible on menus.  Devices without hardware +     * key input will never make shortcuts visible even if this method is passed 'true'. +     * +     * @param shortcutsVisible Whether shortcuts should be visible (if true and a +     *            menu item does not have a shortcut defined, that item will +     *            still NOT show a shortcut) +     */ +    public void setShortcutsVisible(boolean shortcutsVisible) { +        if (mShortcutsVisible == shortcutsVisible) return; + +        setShortcutsVisibleInner(shortcutsVisible); +        onItemsChanged(false); +    } + +    private void setShortcutsVisibleInner(boolean shortcutsVisible) { +        mShortcutsVisible = shortcutsVisible +                && mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS +                && mResources.getBoolean( +                        R.bool.abs__config_showMenuShortcutsWhenKeyboardPresent); +    } + +    /** +     * @return Whether shortcuts should be visible on menus. +     */ +    public boolean isShortcutsVisible() { +        return mShortcutsVisible; +    } + +    Resources getResources() { +        return mResources; +    } + +    public Context getContext() { +        return mContext; +    } + +    boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) { +        return mCallback != null && mCallback.onMenuItemSelected(menu, item); +    } + +    /** +     * Dispatch a mode change event to this menu's callback. +     */ +    public void changeMenuMode() { +        if (mCallback != null) { +            mCallback.onMenuModeChange(this); +        } +    } + +    private static int findInsertIndex(ArrayList<MenuItemImpl> items, int ordering) { +        for (int i = items.size() - 1; i >= 0; i--) { +            MenuItemImpl item = items.get(i); +            if (item.getOrdering() <= ordering) { +                return i + 1; +            } +        } + +        return 0; +    } + +    public boolean performShortcut(int keyCode, KeyEvent event, int flags) { +        final MenuItemImpl item = findItemWithShortcutForKey(keyCode, event); + +        boolean handled = false; + +        if (item != null) { +            handled = performItemAction(item, flags); +        } + +        if ((flags & FLAG_ALWAYS_PERFORM_CLOSE) != 0) { +            close(true); +        } + +        return handled; +    } + +    /* +     * This function will return all the menu and sub-menu items that can +     * be directly (the shortcut directly corresponds) and indirectly +     * (the ALT-enabled char corresponds to the shortcut) associated +     * with the keyCode. +     */ +    @SuppressWarnings("deprecation") +    void findItemsWithShortcutForKey(List<MenuItemImpl> items, int keyCode, KeyEvent event) { +        final boolean qwerty = isQwertyMode(); +        final int metaState = event.getMetaState(); +        final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData(); +        // Get the chars associated with the keyCode (i.e using any chording combo) +        final boolean isKeyCodeMapped = event.getKeyData(possibleChars); +        // The delete key is not mapped to '\b' so we treat it specially +        if (!isKeyCodeMapped && (keyCode != KeyEvent.KEYCODE_DEL)) { +            return; +        } + +        // Look for an item whose shortcut is this key. +        final int N = mItems.size(); +        for (int i = 0; i < N; i++) { +            MenuItemImpl item = mItems.get(i); +            if (item.hasSubMenu()) { +                ((MenuBuilder)item.getSubMenu()).findItemsWithShortcutForKey(items, keyCode, event); +            } +            final char shortcutChar = qwerty ? item.getAlphabeticShortcut() : item.getNumericShortcut(); +            if (((metaState & (KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON)) == 0) && +                  (shortcutChar != 0) && +                  (shortcutChar == possibleChars.meta[0] +                      || shortcutChar == possibleChars.meta[2] +                      || (qwerty && shortcutChar == '\b' && +                          keyCode == KeyEvent.KEYCODE_DEL)) && +                  item.isEnabled()) { +                items.add(item); +            } +        } +    } + +    /* +     * We want to return the menu item associated with the key, but if there is no +     * ambiguity (i.e. there is only one menu item corresponding to the key) we want +     * to return it even if it's not an exact match; this allow the user to +     * _not_ use the ALT key for example, making the use of shortcuts slightly more +     * user-friendly. An example is on the G1, '!' and '1' are on the same key, and +     * in Gmail, Menu+1 will trigger Menu+! (the actual shortcut). +     * +     * On the other hand, if two (or more) shortcuts corresponds to the same key, +     * we have to only return the exact match. +     */ +    @SuppressWarnings("deprecation") +    MenuItemImpl findItemWithShortcutForKey(int keyCode, KeyEvent event) { +        // Get all items that can be associated directly or indirectly with the keyCode +        ArrayList<MenuItemImpl> items = mTempShortcutItemList; +        items.clear(); +        findItemsWithShortcutForKey(items, keyCode, event); + +        if (items.isEmpty()) { +            return null; +        } + +        final int metaState = event.getMetaState(); +        final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData(); +        // Get the chars associated with the keyCode (i.e using any chording combo) +        event.getKeyData(possibleChars); + +        // If we have only one element, we can safely returns it +        final int size = items.size(); +        if (size == 1) { +            return items.get(0); +        } + +        final boolean qwerty = isQwertyMode(); +        // If we found more than one item associated with the key, +        // we have to return the exact match +        for (int i = 0; i < size; i++) { +            final MenuItemImpl item = items.get(i); +            final char shortcutChar = qwerty ? item.getAlphabeticShortcut() : +                    item.getNumericShortcut(); +            if ((shortcutChar == possibleChars.meta[0] && +                    (metaState & KeyEvent.META_ALT_ON) == 0) +                || (shortcutChar == possibleChars.meta[2] && +                    (metaState & KeyEvent.META_ALT_ON) != 0) +                || (qwerty && shortcutChar == '\b' && +                    keyCode == KeyEvent.KEYCODE_DEL)) { +                return item; +            } +        } +        return null; +    } + +    public boolean performIdentifierAction(int id, int flags) { +        // Look for an item whose identifier is the id. +        return performItemAction(findItem(id), flags); +    } + +    public boolean performItemAction(MenuItem item, int flags) { +        MenuItemImpl itemImpl = (MenuItemImpl) item; + +        if (itemImpl == null || !itemImpl.isEnabled()) { +            return false; +        } + +        boolean invoked = itemImpl.invoke(); + +        if (itemImpl.hasCollapsibleActionView()) { +            invoked |= itemImpl.expandActionView(); +            if (invoked) close(true); +        } else if (item.hasSubMenu()) { +            close(false); + +            final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu(); +            final ActionProvider provider = item.getActionProvider(); +            if (provider != null && provider.hasSubMenu()) { +                provider.onPrepareSubMenu(subMenu); +            } +            invoked |= dispatchSubMenuSelected(subMenu); +            if (!invoked) close(true); +        } else { +            if ((flags & FLAG_PERFORM_NO_CLOSE) == 0) { +                close(true); +            } +        } + +        return invoked; +    } + +    /** +     * Closes the visible menu. +     * +     * @param allMenusAreClosing Whether the menus are completely closing (true), +     *            or whether there is another menu coming in this menu's place +     *            (false). For example, if the menu is closing because a +     *            sub menu is about to be shown, <var>allMenusAreClosing</var> +     *            is false. +     */ +    final void close(boolean allMenusAreClosing) { +        if (mIsClosing) return; + +        mIsClosing = true; +        for (WeakReference<MenuPresenter> ref : mPresenters) { +            final MenuPresenter presenter = ref.get(); +            if (presenter == null) { +                mPresenters.remove(ref); +            } else { +                presenter.onCloseMenu(this, allMenusAreClosing); +            } +        } +        mIsClosing = false; +    } + +    /** {@inheritDoc} */ +    public void close() { +        close(true); +    } + +    /** +     * Called when an item is added or removed. +     * +     * @param structureChanged true if the menu structure changed, +     *                         false if only item properties changed. +     *                         (Visibility is a structural property since it affects layout.) +     */ +    void onItemsChanged(boolean structureChanged) { +        if (!mPreventDispatchingItemsChanged) { +            if (structureChanged) { +                mIsVisibleItemsStale = true; +                mIsActionItemsStale = true; +            } + +            dispatchPresenterUpdate(structureChanged); +        } else { +            mItemsChangedWhileDispatchPrevented = true; +        } +    } + +    /** +     * Stop dispatching item changed events to presenters until +     * {@link #startDispatchingItemsChanged()} is called. Useful when +     * many menu operations are going to be performed as a batch. +     */ +    public void stopDispatchingItemsChanged() { +        if (!mPreventDispatchingItemsChanged) { +            mPreventDispatchingItemsChanged = true; +            mItemsChangedWhileDispatchPrevented = false; +        } +    } + +    public void startDispatchingItemsChanged() { +        mPreventDispatchingItemsChanged = false; + +        if (mItemsChangedWhileDispatchPrevented) { +            mItemsChangedWhileDispatchPrevented = false; +            onItemsChanged(true); +        } +    } + +    /** +     * Called by {@link MenuItemImpl} when its visible flag is changed. +     * @param item The item that has gone through a visibility change. +     */ +    void onItemVisibleChanged(MenuItemImpl item) { +        // Notify of items being changed +        mIsVisibleItemsStale = true; +        onItemsChanged(true); +    } + +    /** +     * Called by {@link MenuItemImpl} when its action request status is changed. +     * @param item The item that has gone through a change in action request status. +     */ +    void onItemActionRequestChanged(MenuItemImpl item) { +        // Notify of items being changed +        mIsActionItemsStale = true; +        onItemsChanged(true); +    } + +    ArrayList<MenuItemImpl> getVisibleItems() { +        if (!mIsVisibleItemsStale) return mVisibleItems; + +        // Refresh the visible items +        mVisibleItems.clear(); + +        final int itemsSize = mItems.size(); +        MenuItemImpl item; +        for (int i = 0; i < itemsSize; i++) { +            item = mItems.get(i); +            if (item.isVisible()) mVisibleItems.add(item); +        } + +        mIsVisibleItemsStale = false; +        mIsActionItemsStale = true; + +        return mVisibleItems; +    } + +    /** +     * This method determines which menu items get to be 'action items' that will appear +     * in an action bar and which items should be 'overflow items' in a secondary menu. +     * The rules are as follows: +     * +     * <p>Items are considered for inclusion in the order specified within the menu. +     * There is a limit of mMaxActionItems as a total count, optionally including the overflow +     * menu button itself. This is a soft limit; if an item shares a group ID with an item +     * previously included as an action item, the new item will stay with its group and become +     * an action item itself even if it breaks the max item count limit. This is done to +     * limit the conceptual complexity of the items presented within an action bar. Only a few +     * unrelated concepts should be presented to the user in this space, and groups are treated +     * as a single concept. +     * +     * <p>There is also a hard limit of consumed measurable space: mActionWidthLimit. This +     * limit may be broken by a single item that exceeds the remaining space, but no further +     * items may be added. If an item that is part of a group cannot fit within the remaining +     * measured width, the entire group will be demoted to overflow. This is done to ensure room +     * for navigation and other affordances in the action bar as well as reduce general UI clutter. +     * +     * <p>The space freed by demoting a full group cannot be consumed by future menu items. +     * Once items begin to overflow, all future items become overflow items as well. This is +     * to avoid inadvertent reordering that may break the app's intended design. +     */ +    public void flagActionItems() { +        if (!mIsActionItemsStale) { +            return; +        } + +        // Presenters flag action items as needed. +        boolean flagged = false; +        for (WeakReference<MenuPresenter> ref : mPresenters) { +            final MenuPresenter presenter = ref.get(); +            if (presenter == null) { +                mPresenters.remove(ref); +            } else { +                flagged |= presenter.flagActionItems(); +            } +        } + +        if (flagged) { +            mActionItems.clear(); +            mNonActionItems.clear(); +            ArrayList<MenuItemImpl> visibleItems = getVisibleItems(); +            final int itemsSize = visibleItems.size(); +            for (int i = 0; i < itemsSize; i++) { +                MenuItemImpl item = visibleItems.get(i); +                if (item.isActionButton()) { +                    mActionItems.add(item); +                } else { +                    mNonActionItems.add(item); +                } +            } +        } else { +            // Nobody flagged anything, everything is a non-action item. +            // (This happens during a first pass with no action-item presenters.) +            mActionItems.clear(); +            mNonActionItems.clear(); +            mNonActionItems.addAll(getVisibleItems()); +        } +        mIsActionItemsStale = false; +    } + +    ArrayList<MenuItemImpl> getActionItems() { +        flagActionItems(); +        return mActionItems; +    } + +    ArrayList<MenuItemImpl> getNonActionItems() { +        flagActionItems(); +        return mNonActionItems; +    } + +    public void clearHeader() { +        mHeaderIcon = null; +        mHeaderTitle = null; +        mHeaderView = null; + +        onItemsChanged(false); +    } + +    private void setHeaderInternal(final int titleRes, final CharSequence title, final int iconRes, +            final Drawable icon, final View view) { +        final Resources r = getResources(); + +        if (view != null) { +            mHeaderView = view; + +            // If using a custom view, then the title and icon aren't used +            mHeaderTitle = null; +            mHeaderIcon = null; +        } else { +            if (titleRes > 0) { +                mHeaderTitle = r.getText(titleRes); +            } else if (title != null) { +                mHeaderTitle = title; +            } + +            if (iconRes > 0) { +                mHeaderIcon = r.getDrawable(iconRes); +            } else if (icon != null) { +                mHeaderIcon = icon; +            } + +            // If using the title or icon, then a custom view isn't used +            mHeaderView = null; +        } + +        // Notify of change +        onItemsChanged(false); +    } + +    /** +     * Sets the header's title. This replaces the header view. Called by the +     * builder-style methods of subclasses. +     * +     * @param title The new title. +     * @return This MenuBuilder so additional setters can be called. +     */ +    protected MenuBuilder setHeaderTitleInt(CharSequence title) { +        setHeaderInternal(0, title, 0, null, null); +        return this; +    } + +    /** +     * Sets the header's title. This replaces the header view. Called by the +     * builder-style methods of subclasses. +     * +     * @param titleRes The new title (as a resource ID). +     * @return This MenuBuilder so additional setters can be called. +     */ +    protected MenuBuilder setHeaderTitleInt(int titleRes) { +        setHeaderInternal(titleRes, null, 0, null, null); +        return this; +    } + +    /** +     * Sets the header's icon. This replaces the header view. Called by the +     * builder-style methods of subclasses. +     * +     * @param icon The new icon. +     * @return This MenuBuilder so additional setters can be called. +     */ +    protected MenuBuilder setHeaderIconInt(Drawable icon) { +        setHeaderInternal(0, null, 0, icon, null); +        return this; +    } + +    /** +     * Sets the header's icon. This replaces the header view. Called by the +     * builder-style methods of subclasses. +     * +     * @param iconRes The new icon (as a resource ID). +     * @return This MenuBuilder so additional setters can be called. +     */ +    protected MenuBuilder setHeaderIconInt(int iconRes) { +        setHeaderInternal(0, null, iconRes, null, null); +        return this; +    } + +    /** +     * Sets the header's view. This replaces the title and icon. Called by the +     * builder-style methods of subclasses. +     * +     * @param view The new view. +     * @return This MenuBuilder so additional setters can be called. +     */ +    protected MenuBuilder setHeaderViewInt(View view) { +        setHeaderInternal(0, null, 0, null, view); +        return this; +    } + +    public CharSequence getHeaderTitle() { +        return mHeaderTitle; +    } + +    public Drawable getHeaderIcon() { +        return mHeaderIcon; +    } + +    public View getHeaderView() { +        return mHeaderView; +    } + +    /** +     * Gets the root menu (if this is a submenu, find its root menu). +     * @return The root menu. +     */ +    public MenuBuilder getRootMenu() { +        return this; +    } + +    /** +     * Sets the current menu info that is set on all items added to this menu +     * (until this is called again with different menu info, in which case that +     * one will be added to all subsequent item additions). +     * +     * @param menuInfo The extra menu information to add. +     */ +    public void setCurrentMenuInfo(ContextMenuInfo menuInfo) { +        mCurrentMenuInfo = menuInfo; +    } + +    void setOptionalIconsVisible(boolean visible) { +        mOptionalIconsVisible = visible; +    } + +    boolean getOptionalIconsVisible() { +        return mOptionalIconsVisible; +    } + +    public boolean expandItemActionView(MenuItemImpl item) { +        if (mPresenters.isEmpty()) return false; + +        boolean expanded = false; + +        stopDispatchingItemsChanged(); +        for (WeakReference<MenuPresenter> ref : mPresenters) { +            final MenuPresenter presenter = ref.get(); +            if (presenter == null) { +                mPresenters.remove(ref); +            } else if ((expanded = presenter.expandItemActionView(this, item))) { +                break; +            } +        } +        startDispatchingItemsChanged(); + +        if (expanded) { +            mExpandedItem = item; +        } +        return expanded; +    } + +    public boolean collapseItemActionView(MenuItemImpl item) { +        if (mPresenters.isEmpty() || mExpandedItem != item) return false; + +        boolean collapsed = false; + +        stopDispatchingItemsChanged(); +        for (WeakReference<MenuPresenter> ref : mPresenters) { +            final MenuPresenter presenter = ref.get(); +            if (presenter == null) { +                mPresenters.remove(ref); +            } else if ((collapsed = presenter.collapseItemActionView(this, item))) { +                break; +            } +        } +        startDispatchingItemsChanged(); + +        if (collapsed) { +            mExpandedItem = null; +        } +        return collapsed; +    } + +    public MenuItemImpl getExpandedItem() { +        return mExpandedItem; +    } + +    public boolean bindNativeOverflow(android.view.Menu menu, android.view.MenuItem.OnMenuItemClickListener listener, HashMap<android.view.MenuItem, MenuItemImpl> map) { +        final List<MenuItemImpl> nonActionItems = getNonActionItems(); +        if (nonActionItems == null || nonActionItems.size() == 0) { +            return false; +        } + +        boolean visible = false; +        menu.clear(); +        for (MenuItemImpl nonActionItem : nonActionItems) { +            if (!nonActionItem.isVisible()) { +                continue; +            } +            visible = true; + +            android.view.MenuItem nativeItem; +            if (nonActionItem.hasSubMenu()) { +                android.view.SubMenu nativeSub = menu.addSubMenu(nonActionItem.getGroupId(), nonActionItem.getItemId(), +                        nonActionItem.getOrder(), nonActionItem.getTitle()); + +                SubMenuBuilder subMenu = (SubMenuBuilder)nonActionItem.getSubMenu(); +                for (MenuItemImpl subItem : subMenu.getVisibleItems()) { +                    android.view.MenuItem nativeSubItem = nativeSub.add(subItem.getGroupId(), subItem.getItemId(), +                            subItem.getOrder(), subItem.getTitle()); + +                    nativeSubItem.setIcon(subItem.getIcon()); +                    nativeSubItem.setOnMenuItemClickListener(listener); +                    nativeSubItem.setEnabled(subItem.isEnabled()); +                    nativeSubItem.setIntent(subItem.getIntent()); +                    nativeSubItem.setNumericShortcut(subItem.getNumericShortcut()); +                    nativeSubItem.setAlphabeticShortcut(subItem.getAlphabeticShortcut()); +                    nativeSubItem.setTitleCondensed(subItem.getTitleCondensed()); +                    nativeSubItem.setCheckable(subItem.isCheckable()); +                    nativeSubItem.setChecked(subItem.isChecked()); + +                    if (subItem.isExclusiveCheckable()) { +                        nativeSub.setGroupCheckable(subItem.getGroupId(), true, true); +                    } + +                    map.put(nativeSubItem, subItem); +                } + +                nativeItem = nativeSub.getItem(); +            } else { +                nativeItem = menu.add(nonActionItem.getGroupId(), nonActionItem.getItemId(), +                        nonActionItem.getOrder(), nonActionItem.getTitle()); +            } +            nativeItem.setIcon(nonActionItem.getIcon()); +            nativeItem.setOnMenuItemClickListener(listener); +            nativeItem.setEnabled(nonActionItem.isEnabled()); +            nativeItem.setIntent(nonActionItem.getIntent()); +            nativeItem.setNumericShortcut(nonActionItem.getNumericShortcut()); +            nativeItem.setAlphabeticShortcut(nonActionItem.getAlphabeticShortcut()); +            nativeItem.setTitleCondensed(nonActionItem.getTitleCondensed()); +            nativeItem.setCheckable(nonActionItem.isCheckable()); +            nativeItem.setChecked(nonActionItem.isChecked()); + +            if (nonActionItem.isExclusiveCheckable()) { +                menu.setGroupCheckable(nonActionItem.getGroupId(), true, true); +            } + +            map.put(nativeItem, nonActionItem); +        } +        return visible; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java new file mode 100644 index 000000000..f5359fb40 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java @@ -0,0 +1,647 @@ +/* + * Copyright (C) 2006 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.actionbarsherlock.internal.view.menu; + +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewDebug; +import android.widget.LinearLayout; + +import com.actionbarsherlock.view.ActionProvider; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.SubMenu; + +/** + * @hide + */ +public final class MenuItemImpl implements MenuItem { +    private static final String TAG = "MenuItemImpl"; + +    private static final int SHOW_AS_ACTION_MASK = SHOW_AS_ACTION_NEVER | +            SHOW_AS_ACTION_IF_ROOM | +            SHOW_AS_ACTION_ALWAYS; + +    private final int mId; +    private final int mGroup; +    private final int mCategoryOrder; +    private final int mOrdering; +    private CharSequence mTitle; +    private CharSequence mTitleCondensed; +    private Intent mIntent; +    private char mShortcutNumericChar; +    private char mShortcutAlphabeticChar; + +    /** The icon's drawable which is only created as needed */ +    private Drawable mIconDrawable; +    /** +     * The icon's resource ID which is used to get the Drawable when it is +     * needed (if the Drawable isn't already obtained--only one of the two is +     * needed). +     */ +    private int mIconResId = NO_ICON; + +    /** The menu to which this item belongs */ +    private MenuBuilder mMenu; +    /** If this item should launch a sub menu, this is the sub menu to launch */ +    private SubMenuBuilder mSubMenu; + +    private Runnable mItemCallback; +    private MenuItem.OnMenuItemClickListener mClickListener; + +    private int mFlags = ENABLED; +    private static final int CHECKABLE      = 0x00000001; +    private static final int CHECKED        = 0x00000002; +    private static final int EXCLUSIVE      = 0x00000004; +    private static final int HIDDEN         = 0x00000008; +    private static final int ENABLED        = 0x00000010; +    private static final int IS_ACTION      = 0x00000020; + +    private int mShowAsAction = SHOW_AS_ACTION_NEVER; + +    private View mActionView; +    private ActionProvider mActionProvider; +    private OnActionExpandListener mOnActionExpandListener; +    private boolean mIsActionViewExpanded = false; + +    /** Used for the icon resource ID if this item does not have an icon */ +    static final int NO_ICON = 0; + +    /** +     * Current use case is for context menu: Extra information linked to the +     * View that added this item to the context menu. +     */ +    private ContextMenuInfo mMenuInfo; + +    private static String sPrependShortcutLabel; +    private static String sEnterShortcutLabel; +    private static String sDeleteShortcutLabel; +    private static String sSpaceShortcutLabel; + + +    /** +     * Instantiates this menu item. +     * +     * @param menu +     * @param group Item ordering grouping control. The item will be added after +     *            all other items whose order is <= this number, and before any +     *            that are larger than it. This can also be used to define +     *            groups of items for batch state changes. Normally use 0. +     * @param id Unique item ID. Use 0 if you do not need a unique ID. +     * @param categoryOrder The ordering for this item. +     * @param title The text to display for the item. +     */ +    MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering, +            CharSequence title, int showAsAction) { + +        /* TODO if (sPrependShortcutLabel == null) { +            // This is instantiated from the UI thread, so no chance of sync issues +            sPrependShortcutLabel = menu.getContext().getResources().getString( +                    com.android.internal.R.string.prepend_shortcut_label); +            sEnterShortcutLabel = menu.getContext().getResources().getString( +                    com.android.internal.R.string.menu_enter_shortcut_label); +            sDeleteShortcutLabel = menu.getContext().getResources().getString( +                    com.android.internal.R.string.menu_delete_shortcut_label); +            sSpaceShortcutLabel = menu.getContext().getResources().getString( +                    com.android.internal.R.string.menu_space_shortcut_label); +        }*/ + +        mMenu = menu; +        mId = id; +        mGroup = group; +        mCategoryOrder = categoryOrder; +        mOrdering = ordering; +        mTitle = title; +        mShowAsAction = showAsAction; +    } + +    /** +     * Invokes the item by calling various listeners or callbacks. +     * +     * @return true if the invocation was handled, false otherwise +     */ +    public boolean invoke() { +        if (mClickListener != null && +            mClickListener.onMenuItemClick(this)) { +            return true; +        } + +        if (mMenu.dispatchMenuItemSelected(mMenu.getRootMenu(), this)) { +            return true; +        } + +        if (mItemCallback != null) { +            mItemCallback.run(); +            return true; +        } + +        if (mIntent != null) { +            try { +                mMenu.getContext().startActivity(mIntent); +                return true; +            } catch (ActivityNotFoundException e) { +                Log.e(TAG, "Can't find activity to handle intent; ignoring", e); +            } +        } + +        if (mActionProvider != null && mActionProvider.onPerformDefaultAction()) { +            return true; +        } + +        return false; +    } + +    public boolean isEnabled() { +        return (mFlags & ENABLED) != 0; +    } + +    public MenuItem setEnabled(boolean enabled) { +        if (enabled) { +            mFlags |= ENABLED; +        } else { +            mFlags &= ~ENABLED; +        } + +        mMenu.onItemsChanged(false); + +        return this; +    } + +    public int getGroupId() { +        return mGroup; +    } + +    @ViewDebug.CapturedViewProperty +    public int getItemId() { +        return mId; +    } + +    public int getOrder() { +        return mCategoryOrder; +    } + +    public int getOrdering() { +        return mOrdering; +    } + +    public Intent getIntent() { +        return mIntent; +    } + +    public MenuItem setIntent(Intent intent) { +        mIntent = intent; +        return this; +    } + +    Runnable getCallback() { +        return mItemCallback; +    } + +    public MenuItem setCallback(Runnable callback) { +        mItemCallback = callback; +        return this; +    } + +    public char getAlphabeticShortcut() { +        return mShortcutAlphabeticChar; +    } + +    public MenuItem setAlphabeticShortcut(char alphaChar) { +        if (mShortcutAlphabeticChar == alphaChar) return this; + +        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar); + +        mMenu.onItemsChanged(false); + +        return this; +    } + +    public char getNumericShortcut() { +        return mShortcutNumericChar; +    } + +    public MenuItem setNumericShortcut(char numericChar) { +        if (mShortcutNumericChar == numericChar) return this; + +        mShortcutNumericChar = numericChar; + +        mMenu.onItemsChanged(false); + +        return this; +    } + +    public MenuItem setShortcut(char numericChar, char alphaChar) { +        mShortcutNumericChar = numericChar; +        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar); + +        mMenu.onItemsChanged(false); + +        return this; +    } + +    /** +     * @return The active shortcut (based on QWERTY-mode of the menu). +     */ +    char getShortcut() { +        return (mMenu.isQwertyMode() ? mShortcutAlphabeticChar : mShortcutNumericChar); +    } + +    /** +     * @return The label to show for the shortcut. This includes the chording +     *         key (for example 'Menu+a'). Also, any non-human readable +     *         characters should be human readable (for example 'Menu+enter'). +     */ +    String getShortcutLabel() { + +        char shortcut = getShortcut(); +        if (shortcut == 0) { +            return ""; +        } + +        StringBuilder sb = new StringBuilder(sPrependShortcutLabel); +        switch (shortcut) { + +            case '\n': +                sb.append(sEnterShortcutLabel); +                break; + +            case '\b': +                sb.append(sDeleteShortcutLabel); +                break; + +            case ' ': +                sb.append(sSpaceShortcutLabel); +                break; + +            default: +                sb.append(shortcut); +                break; +        } + +        return sb.toString(); +    } + +    /** +     * @return Whether this menu item should be showing shortcuts (depends on +     *         whether the menu should show shortcuts and whether this item has +     *         a shortcut defined) +     */ +    boolean shouldShowShortcut() { +        // Show shortcuts if the menu is supposed to show shortcuts AND this item has a shortcut +        return mMenu.isShortcutsVisible() && (getShortcut() != 0); +    } + +    public SubMenu getSubMenu() { +        return mSubMenu; +    } + +    public boolean hasSubMenu() { +        return mSubMenu != null; +    } + +    void setSubMenu(SubMenuBuilder subMenu) { +        mSubMenu = subMenu; + +        subMenu.setHeaderTitle(getTitle()); +    } + +    @ViewDebug.CapturedViewProperty +    public CharSequence getTitle() { +        return mTitle; +    } + +    /** +     * Gets the title for a particular {@link ItemView} +     * +     * @param itemView The ItemView that is receiving the title +     * @return Either the title or condensed title based on what the ItemView +     *         prefers +     */ +    CharSequence getTitleForItemView(MenuView.ItemView itemView) { +        return ((itemView != null) && itemView.prefersCondensedTitle()) +                ? getTitleCondensed() +                : getTitle(); +    } + +    public MenuItem setTitle(CharSequence title) { +        mTitle = title; + +        mMenu.onItemsChanged(false); + +        if (mSubMenu != null) { +            mSubMenu.setHeaderTitle(title); +        } + +        return this; +    } + +    public MenuItem setTitle(int title) { +        return setTitle(mMenu.getContext().getString(title)); +    } + +    public CharSequence getTitleCondensed() { +        return mTitleCondensed != null ? mTitleCondensed : mTitle; +    } + +    public MenuItem setTitleCondensed(CharSequence title) { +        mTitleCondensed = title; + +        // Could use getTitle() in the loop below, but just cache what it would do here +        if (title == null) { +            title = mTitle; +        } + +        mMenu.onItemsChanged(false); + +        return this; +    } + +    public Drawable getIcon() { +        if (mIconDrawable != null) { +            return mIconDrawable; +        } + +        if (mIconResId != NO_ICON) { +            return mMenu.getResources().getDrawable(mIconResId); +        } + +        return null; +    } + +    public MenuItem setIcon(Drawable icon) { +        mIconResId = NO_ICON; +        mIconDrawable = icon; +        mMenu.onItemsChanged(false); + +        return this; +    } + +    public MenuItem setIcon(int iconResId) { +        mIconDrawable = null; +        mIconResId = iconResId; + +        // If we have a view, we need to push the Drawable to them +        mMenu.onItemsChanged(false); + +        return this; +    } + +    public boolean isCheckable() { +        return (mFlags & CHECKABLE) == CHECKABLE; +    } + +    public MenuItem setCheckable(boolean checkable) { +        final int oldFlags = mFlags; +        mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0); +        if (oldFlags != mFlags) { +            mMenu.onItemsChanged(false); +        } + +        return this; +    } + +    public void setExclusiveCheckable(boolean exclusive) { +        mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0); +    } + +    public boolean isExclusiveCheckable() { +        return (mFlags & EXCLUSIVE) != 0; +    } + +    public boolean isChecked() { +        return (mFlags & CHECKED) == CHECKED; +    } + +    public MenuItem setChecked(boolean checked) { +        if ((mFlags & EXCLUSIVE) != 0) { +            // Call the method on the Menu since it knows about the others in this +            // exclusive checkable group +            mMenu.setExclusiveItemChecked(this); +        } else { +            setCheckedInt(checked); +        } + +        return this; +    } + +    void setCheckedInt(boolean checked) { +        final int oldFlags = mFlags; +        mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0); +        if (oldFlags != mFlags) { +            mMenu.onItemsChanged(false); +        } +    } + +    public boolean isVisible() { +        return (mFlags & HIDDEN) == 0; +    } + +    /** +     * Changes the visibility of the item. This method DOES NOT notify the +     * parent menu of a change in this item, so this should only be called from +     * methods that will eventually trigger this change.  If unsure, use {@link #setVisible(boolean)} +     * instead. +     * +     * @param shown Whether to show (true) or hide (false). +     * @return Whether the item's shown state was changed +     */ +    boolean setVisibleInt(boolean shown) { +        final int oldFlags = mFlags; +        mFlags = (mFlags & ~HIDDEN) | (shown ? 0 : HIDDEN); +        return oldFlags != mFlags; +    } + +    public MenuItem setVisible(boolean shown) { +        // Try to set the shown state to the given state. If the shown state was changed +        // (i.e. the previous state isn't the same as given state), notify the parent menu that +        // the shown state has changed for this item +        if (setVisibleInt(shown)) mMenu.onItemVisibleChanged(this); + +        return this; +    } + +   public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener clickListener) { +        mClickListener = clickListener; +        return this; +    } + +    @Override +    public String toString() { +        return mTitle.toString(); +    } + +    void setMenuInfo(ContextMenuInfo menuInfo) { +        mMenuInfo = menuInfo; +    } + +    public ContextMenuInfo getMenuInfo() { +        return mMenuInfo; +    } + +    public void actionFormatChanged() { +        mMenu.onItemActionRequestChanged(this); +    } + +    /** +     * @return Whether the menu should show icons for menu items. +     */ +    public boolean shouldShowIcon() { +        return mMenu.getOptionalIconsVisible(); +    } + +    public boolean isActionButton() { +        return (mFlags & IS_ACTION) == IS_ACTION; +    } + +    public boolean requestsActionButton() { +        return (mShowAsAction & SHOW_AS_ACTION_IF_ROOM) == SHOW_AS_ACTION_IF_ROOM; +    } + +    public boolean requiresActionButton() { +        return (mShowAsAction & SHOW_AS_ACTION_ALWAYS) == SHOW_AS_ACTION_ALWAYS; +    } + +    public void setIsActionButton(boolean isActionButton) { +        if (isActionButton) { +            mFlags |= IS_ACTION; +        } else { +            mFlags &= ~IS_ACTION; +        } +    } + +    public boolean showsTextAsAction() { +        return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT; +    } + +    public void setShowAsAction(int actionEnum) { +        switch (actionEnum & SHOW_AS_ACTION_MASK) { +            case SHOW_AS_ACTION_ALWAYS: +            case SHOW_AS_ACTION_IF_ROOM: +            case SHOW_AS_ACTION_NEVER: +                // Looks good! +                break; + +            default: +                // Mutually exclusive options selected! +                throw new IllegalArgumentException("SHOW_AS_ACTION_ALWAYS, SHOW_AS_ACTION_IF_ROOM," +                        + " and SHOW_AS_ACTION_NEVER are mutually exclusive."); +        } +        mShowAsAction = actionEnum; +        mMenu.onItemActionRequestChanged(this); +    } + +    public MenuItem setActionView(View view) { +        mActionView = view; +        mActionProvider = null; +        if (view != null && view.getId() == View.NO_ID && mId > 0) { +            view.setId(mId); +        } +        mMenu.onItemActionRequestChanged(this); +        return this; +    } + +    public MenuItem setActionView(int resId) { +        final Context context = mMenu.getContext(); +        final LayoutInflater inflater = LayoutInflater.from(context); +        setActionView(inflater.inflate(resId, new LinearLayout(context), false)); +        return this; +    } + +    public View getActionView() { +        if (mActionView != null) { +            return mActionView; +        } else if (mActionProvider != null) { +            mActionView = mActionProvider.onCreateActionView(); +            return mActionView; +        } else { +            return null; +        } +    } + +    public ActionProvider getActionProvider() { +        return mActionProvider; +    } + +    public MenuItem setActionProvider(ActionProvider actionProvider) { +        mActionView = null; +        mActionProvider = actionProvider; +        mMenu.onItemsChanged(true); // Measurement can be changed +        return this; +    } + +    @Override +    public MenuItem setShowAsActionFlags(int actionEnum) { +        setShowAsAction(actionEnum); +        return this; +    } + +    @Override +    public boolean expandActionView() { +        if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0 || mActionView == null) { +            return false; +        } + +        if (mOnActionExpandListener == null || +                mOnActionExpandListener.onMenuItemActionExpand(this)) { +            return mMenu.expandItemActionView(this); +        } + +        return false; +    } + +    @Override +    public boolean collapseActionView() { +        if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0) { +            return false; +        } +        if (mActionView == null) { +            // We're already collapsed if we have no action view. +            return true; +        } + +        if (mOnActionExpandListener == null || +                mOnActionExpandListener.onMenuItemActionCollapse(this)) { +            return mMenu.collapseItemActionView(this); +        } + +        return false; +    } + +    @Override +    public MenuItem setOnActionExpandListener(OnActionExpandListener listener) { +        mOnActionExpandListener = listener; +        return this; +    } + +    public boolean hasCollapsibleActionView() { +        return (mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0 && mActionView != null; +    } + +    public void setActionViewExpanded(boolean isExpanded) { +        mIsActionViewExpanded = isExpanded; +        mMenu.onItemsChanged(false); +    } + +    public boolean isActionViewExpanded() { +        return mIsActionViewExpanded; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java new file mode 100644 index 000000000..aaf2997b7 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java @@ -0,0 +1,310 @@ +package com.actionbarsherlock.internal.view.menu; + +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.View; +import com.actionbarsherlock.internal.view.ActionProviderWrapper; +import com.actionbarsherlock.internal.widget.CollapsibleActionViewWrapper; +import com.actionbarsherlock.view.ActionProvider; +import com.actionbarsherlock.view.CollapsibleActionView; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.SubMenu; + +public class MenuItemWrapper implements MenuItem, android.view.MenuItem.OnMenuItemClickListener { +    private final android.view.MenuItem mNativeItem; +    private SubMenu mSubMenu = null; +    private OnMenuItemClickListener mMenuItemClickListener = null; +    private OnActionExpandListener mActionExpandListener = null; +    private android.view.MenuItem.OnActionExpandListener mNativeActionExpandListener = null; + + +    public MenuItemWrapper(android.view.MenuItem nativeItem) { +        if (nativeItem == null) { +            throw new IllegalStateException("Wrapped menu item cannot be null."); +        } +        mNativeItem = nativeItem; +    } + + +    @Override +    public int getItemId() { +        return mNativeItem.getItemId(); +    } + +    @Override +    public int getGroupId() { +        return mNativeItem.getGroupId(); +    } + +    @Override +    public int getOrder() { +        return mNativeItem.getOrder(); +    } + +    @Override +    public MenuItem setTitle(CharSequence title) { +        mNativeItem.setTitle(title); +        return this; +    } + +    @Override +    public MenuItem setTitle(int title) { +        mNativeItem.setTitle(title); +        return this; +    } + +    @Override +    public CharSequence getTitle() { +        return mNativeItem.getTitle(); +    } + +    @Override +    public MenuItem setTitleCondensed(CharSequence title) { +        mNativeItem.setTitleCondensed(title); +        return this; +    } + +    @Override +    public CharSequence getTitleCondensed() { +        return mNativeItem.getTitleCondensed(); +    } + +    @Override +    public MenuItem setIcon(Drawable icon) { +        mNativeItem.setIcon(icon); +        return this; +    } + +    @Override +    public MenuItem setIcon(int iconRes) { +        mNativeItem.setIcon(iconRes); +        return this; +    } + +    @Override +    public Drawable getIcon() { +        return mNativeItem.getIcon(); +    } + +    @Override +    public MenuItem setIntent(Intent intent) { +        mNativeItem.setIntent(intent); +        return this; +    } + +    @Override +    public Intent getIntent() { +        return mNativeItem.getIntent(); +    } + +    @Override +    public MenuItem setShortcut(char numericChar, char alphaChar) { +        mNativeItem.setShortcut(numericChar, alphaChar); +        return this; +    } + +    @Override +    public MenuItem setNumericShortcut(char numericChar) { +        mNativeItem.setNumericShortcut(numericChar); +        return this; +    } + +    @Override +    public char getNumericShortcut() { +        return mNativeItem.getNumericShortcut(); +    } + +    @Override +    public MenuItem setAlphabeticShortcut(char alphaChar) { +        mNativeItem.setAlphabeticShortcut(alphaChar); +        return this; +    } + +    @Override +    public char getAlphabeticShortcut() { +        return mNativeItem.getAlphabeticShortcut(); +    } + +    @Override +    public MenuItem setCheckable(boolean checkable) { +        mNativeItem.setCheckable(checkable); +        return this; +    } + +    @Override +    public boolean isCheckable() { +        return mNativeItem.isCheckable(); +    } + +    @Override +    public MenuItem setChecked(boolean checked) { +        mNativeItem.setChecked(checked); +        return this; +    } + +    @Override +    public boolean isChecked() { +        return mNativeItem.isChecked(); +    } + +    @Override +    public MenuItem setVisible(boolean visible) { +        mNativeItem.setVisible(visible); +        return this; +    } + +    @Override +    public boolean isVisible() { +        return mNativeItem.isVisible(); +    } + +    @Override +    public MenuItem setEnabled(boolean enabled) { +        mNativeItem.setEnabled(enabled); +        return this; +    } + +    @Override +    public boolean isEnabled() { +        return mNativeItem.isEnabled(); +    } + +    @Override +    public boolean hasSubMenu() { +        return mNativeItem.hasSubMenu(); +    } + +    @Override +    public SubMenu getSubMenu() { +        if (hasSubMenu() && (mSubMenu == null)) { +            mSubMenu = new SubMenuWrapper(mNativeItem.getSubMenu()); +        } +        return mSubMenu; +    } + +    @Override +    public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) { +        mMenuItemClickListener = menuItemClickListener; +        //Register ourselves as the listener to proxy +        mNativeItem.setOnMenuItemClickListener(this); +        return this; +    } + +    @Override +    public boolean onMenuItemClick(android.view.MenuItem item) { +        if (mMenuItemClickListener != null) { +            return mMenuItemClickListener.onMenuItemClick(this); +        } +        return false; +    } + +    @Override +    public ContextMenuInfo getMenuInfo() { +        return mNativeItem.getMenuInfo(); +    } + +    @Override +    public void setShowAsAction(int actionEnum) { +        mNativeItem.setShowAsAction(actionEnum); +    } + +    @Override +    public MenuItem setShowAsActionFlags(int actionEnum) { +        mNativeItem.setShowAsActionFlags(actionEnum); +        return this; +    } + +    @Override +    public MenuItem setActionView(View view) { +        if (view != null && view instanceof CollapsibleActionView) { +            view = new CollapsibleActionViewWrapper(view); +        } +        mNativeItem.setActionView(view); +        return this; +    } + +    @Override +    public MenuItem setActionView(int resId) { +        //Allow the native menu to inflate the resource +        mNativeItem.setActionView(resId); +        if (resId != 0) { +            //Get newly created view +            View view = mNativeItem.getActionView(); +            if (view instanceof CollapsibleActionView) { +                //Wrap it and re-set it +                mNativeItem.setActionView(new CollapsibleActionViewWrapper(view)); +            } +        } +        return this; +    } + +    @Override +    public View getActionView() { +        View actionView = mNativeItem.getActionView(); +        if (actionView instanceof CollapsibleActionViewWrapper) { +            return ((CollapsibleActionViewWrapper)actionView).unwrap(); +        } +        return actionView; +    } + +    @Override +    public MenuItem setActionProvider(ActionProvider actionProvider) { +        mNativeItem.setActionProvider(new ActionProviderWrapper(actionProvider)); +        return this; +    } + +    @Override +    public ActionProvider getActionProvider() { +        android.view.ActionProvider nativeProvider = mNativeItem.getActionProvider(); +        if (nativeProvider != null && nativeProvider instanceof ActionProviderWrapper) { +            return ((ActionProviderWrapper)nativeProvider).unwrap(); +        } +        return null; +    } + +    @Override +    public boolean expandActionView() { +        return mNativeItem.expandActionView(); +    } + +    @Override +    public boolean collapseActionView() { +        return mNativeItem.collapseActionView(); +    } + +    @Override +    public boolean isActionViewExpanded() { +        return mNativeItem.isActionViewExpanded(); +    } + +    @Override +    public MenuItem setOnActionExpandListener(OnActionExpandListener listener) { +        mActionExpandListener = listener; + +        if (mNativeActionExpandListener == null) { +            mNativeActionExpandListener = new android.view.MenuItem.OnActionExpandListener() { +                @Override +                public boolean onMenuItemActionExpand(android.view.MenuItem menuItem) { +                    if (mActionExpandListener != null) { +                        return mActionExpandListener.onMenuItemActionExpand(MenuItemWrapper.this); +                    } +                    return false; +                } + +                @Override +                public boolean onMenuItemActionCollapse(android.view.MenuItem menuItem) { +                    if (mActionExpandListener != null) { +                        return mActionExpandListener.onMenuItemActionCollapse(MenuItemWrapper.this); +                    } +                    return false; +                } +            }; + +            //Register our inner-class as the listener to proxy method calls +            mNativeItem.setOnActionExpandListener(mNativeActionExpandListener); +        } + +        return this; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java new file mode 100644 index 000000000..f030de310 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.view.menu; + +import java.util.ArrayList; +import android.content.Context; +import android.content.res.Resources; +import android.database.DataSetObserver; +import android.os.Parcelable; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.MeasureSpec; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.ListAdapter; +import android.widget.PopupWindow; +import com.actionbarsherlock.R; +import com.actionbarsherlock.internal.view.View_HasStateListenerSupport; +import com.actionbarsherlock.internal.view.View_OnAttachStateChangeListener; +import com.actionbarsherlock.internal.widget.IcsListPopupWindow; +import com.actionbarsherlock.view.MenuItem; + +/** + * Presents a menu as a small, simple popup anchored to another view. + * @hide + */ +public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener, +        ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener, +        View_OnAttachStateChangeListener, MenuPresenter { +    //UNUSED private static final String TAG = "MenuPopupHelper"; + +    static final int ITEM_LAYOUT = R.layout.abs__popup_menu_item_layout; + +    private Context mContext; +    private LayoutInflater mInflater; +    private IcsListPopupWindow mPopup; +    private MenuBuilder mMenu; +    private int mPopupMaxWidth; +    private View mAnchorView; +    private boolean mOverflowOnly; +    private ViewTreeObserver mTreeObserver; + +    private MenuAdapter mAdapter; + +    private Callback mPresenterCallback; + +    boolean mForceShowIcon; + +    private ViewGroup mMeasureParent; + +    public MenuPopupHelper(Context context, MenuBuilder menu) { +        this(context, menu, null, false); +    } + +    public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) { +        this(context, menu, anchorView, false); +    } + +    public MenuPopupHelper(Context context, MenuBuilder menu, +            View anchorView, boolean overflowOnly) { +        mContext = context; +        mInflater = LayoutInflater.from(context); +        mMenu = menu; +        mOverflowOnly = overflowOnly; + +        final Resources res = context.getResources(); +        mPopupMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2, +                res.getDimensionPixelSize(R.dimen.abs__config_prefDialogWidth)); + +        mAnchorView = anchorView; + +        menu.addMenuPresenter(this); +    } + +    public void setAnchorView(View anchor) { +        mAnchorView = anchor; +    } + +    public void setForceShowIcon(boolean forceShow) { +        mForceShowIcon = forceShow; +    } + +    public void show() { +        if (!tryShow()) { +            throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); +        } +    } + +    public boolean tryShow() { +        mPopup = new IcsListPopupWindow(mContext, null, R.attr.popupMenuStyle); +        mPopup.setOnDismissListener(this); +        mPopup.setOnItemClickListener(this); + +        mAdapter = new MenuAdapter(mMenu); +        mPopup.setAdapter(mAdapter); +        mPopup.setModal(true); + +        View anchor = mAnchorView; +        if (anchor != null) { +            final boolean addGlobalListener = mTreeObserver == null; +            mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest +            if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this); +            ((View_HasStateListenerSupport)anchor).addOnAttachStateChangeListener(this); +            mPopup.setAnchorView(anchor); +        } else { +            return false; +        } + +        mPopup.setContentWidth(Math.min(measureContentWidth(mAdapter), mPopupMaxWidth)); +        mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); +        mPopup.show(); +        mPopup.getListView().setOnKeyListener(this); +        return true; +    } + +    public void dismiss() { +        if (isShowing()) { +            mPopup.dismiss(); +        } +    } + +    public void onDismiss() { +        mPopup = null; +        mMenu.close(); +        if (mTreeObserver != null) { +            if (!mTreeObserver.isAlive()) mTreeObserver = mAnchorView.getViewTreeObserver(); +            mTreeObserver.removeGlobalOnLayoutListener(this); +            mTreeObserver = null; +        } +        ((View_HasStateListenerSupport)mAnchorView).removeOnAttachStateChangeListener(this); +    } + +    public boolean isShowing() { +        return mPopup != null && mPopup.isShowing(); +    } + +    @Override +    public void onItemClick(AdapterView<?> parent, View view, int position, long id) { +        MenuAdapter adapter = mAdapter; +        adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0); +    } + +    public boolean onKey(View v, int keyCode, KeyEvent event) { +        if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) { +            dismiss(); +            return true; +        } +        return false; +    } + +    private int measureContentWidth(ListAdapter adapter) { +        // Menus don't tend to be long, so this is more sane than it looks. +        int width = 0; +        View itemView = null; +        int itemType = 0; +        final int widthMeasureSpec = +            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); +        final int heightMeasureSpec = +            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); +        final int count = adapter.getCount(); +        for (int i = 0; i < count; i++) { +            final int positionType = adapter.getItemViewType(i); +            if (positionType != itemType) { +                itemType = positionType; +                itemView = null; +            } +            if (mMeasureParent == null) { +                mMeasureParent = new FrameLayout(mContext); +            } +            itemView = adapter.getView(i, itemView, mMeasureParent); +            itemView.measure(widthMeasureSpec, heightMeasureSpec); +            width = Math.max(width, itemView.getMeasuredWidth()); +        } +        return width; +    } + +    @Override +    public void onGlobalLayout() { +        if (isShowing()) { +            final View anchor = mAnchorView; +            if (anchor == null || !anchor.isShown()) { +                dismiss(); +            } else if (isShowing()) { +                // Recompute window size and position +                mPopup.show(); +            } +        } +    } + +    @Override +    public void onViewAttachedToWindow(View v) { +    } + +    @Override +    public void onViewDetachedFromWindow(View v) { +        if (mTreeObserver != null) { +            if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver(); +            mTreeObserver.removeGlobalOnLayoutListener(this); +        } +        ((View_HasStateListenerSupport)v).removeOnAttachStateChangeListener(this); +    } + +    @Override +    public void initForMenu(Context context, MenuBuilder menu) { +        // Don't need to do anything; we added as a presenter in the constructor. +    } + +    @Override +    public MenuView getMenuView(ViewGroup root) { +        throw new UnsupportedOperationException("MenuPopupHelpers manage their own views"); +    } + +    @Override +    public void updateMenuView(boolean cleared) { +        if (mAdapter != null) mAdapter.notifyDataSetChanged(); +    } + +    @Override +    public void setCallback(Callback cb) { +        mPresenterCallback = cb; +    } + +    @Override +    public boolean onSubMenuSelected(SubMenuBuilder subMenu) { +        if (subMenu.hasVisibleItems()) { +            MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu, mAnchorView, false); +            subPopup.setCallback(mPresenterCallback); + +            boolean preserveIconSpacing = false; +            final int count = subMenu.size(); +            for (int i = 0; i < count; i++) { +                MenuItem childItem = subMenu.getItem(i); +                if (childItem.isVisible() && childItem.getIcon() != null) { +                    preserveIconSpacing = true; +                    break; +                } +            } +            subPopup.setForceShowIcon(preserveIconSpacing); + +            if (subPopup.tryShow()) { +                if (mPresenterCallback != null) { +                    mPresenterCallback.onOpenSubMenu(subMenu); +                } +                return true; +            } +        } +        return false; +    } + +    @Override +    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { +        // Only care about the (sub)menu we're presenting. +        if (menu != mMenu) return; + +        dismiss(); +        if (mPresenterCallback != null) { +            mPresenterCallback.onCloseMenu(menu, allMenusAreClosing); +        } +    } + +    @Override +    public boolean flagActionItems() { +        return false; +    } + +    public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { +        return false; +    } + +    public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { +        return false; +    } + +    @Override +    public int getId() { +        return 0; +    } + +    @Override +    public Parcelable onSaveInstanceState() { +        return null; +    } + +    @Override +    public void onRestoreInstanceState(Parcelable state) { +    } + +    private class MenuAdapter extends BaseAdapter { +        private MenuBuilder mAdapterMenu; +        private int mExpandedIndex = -1; + +        public MenuAdapter(MenuBuilder menu) { +            mAdapterMenu = menu; +            registerDataSetObserver(new ExpandedIndexObserver()); +            findExpandedIndex(); +        } + +        public int getCount() { +            ArrayList<MenuItemImpl> items = mOverflowOnly ? +                    mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems(); +            if (mExpandedIndex < 0) { +                return items.size(); +            } +            return items.size() - 1; +        } + +        public MenuItemImpl getItem(int position) { +            ArrayList<MenuItemImpl> items = mOverflowOnly ? +                    mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems(); +            if (mExpandedIndex >= 0 && position >= mExpandedIndex) { +                position++; +            } +            return items.get(position); +        } + +        public long getItemId(int position) { +            // Since a menu item's ID is optional, we'll use the position as an +            // ID for the item in the AdapterView +            return position; +        } + +        public View getView(int position, View convertView, ViewGroup parent) { +            if (convertView == null) { +                convertView = mInflater.inflate(ITEM_LAYOUT, parent, false); +            } + +            MenuView.ItemView itemView = (MenuView.ItemView) convertView; +            if (mForceShowIcon) { +                ((ListMenuItemView) convertView).setForceShowIcon(true); +            } +            itemView.initialize(getItem(position), 0); +            return convertView; +        } + +        void findExpandedIndex() { +            final MenuItemImpl expandedItem = mMenu.getExpandedItem(); +            if (expandedItem != null) { +                final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems(); +                final int count = items.size(); +                for (int i = 0; i < count; i++) { +                    final MenuItemImpl item = items.get(i); +                    if (item == expandedItem) { +                        mExpandedIndex = i; +                        return; +                    } +                } +            } +            mExpandedIndex = -1; +        } +    } + +    private class ExpandedIndexObserver extends DataSetObserver { +        @Override +        public void onChanged() { +            mAdapter.findExpandedIndex(); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java new file mode 100644 index 000000000..c3f35472c --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.actionbarsherlock.internal.view.menu; + +import android.content.Context; +import android.os.Parcelable; +import android.view.ViewGroup; + +/** + * A MenuPresenter is responsible for building views for a Menu object. + * It takes over some responsibility from the old style monolithic MenuBuilder class. + */ +public interface MenuPresenter { +    /** +     * Called by menu implementation to notify another component of open/close events. +     */ +    public interface Callback { +        /** +         * Called when a menu is closing. +         * @param menu +         * @param allMenusAreClosing +         */ +        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing); + +        /** +         * Called when a submenu opens. Useful for notifying the application +         * of menu state so that it does not attempt to hide the action bar +         * while a submenu is open or similar. +         * +         * @param subMenu Submenu currently being opened +         * @return true if the Callback will handle presenting the submenu, false if +         *         the presenter should attempt to do so. +         */ +        public boolean onOpenSubMenu(MenuBuilder subMenu); +    } + +    /** +     * Initialize this presenter for the given context and menu. +     * This method is called by MenuBuilder when a presenter is +     * added. See {@link MenuBuilder#addMenuPresenter(MenuPresenter)} +     * +     * @param context Context for this presenter; used for view creation and resource management +     * @param menu Menu to host +     */ +    public void initForMenu(Context context, MenuBuilder menu); + +    /** +     * Retrieve a MenuView to display the menu specified in +     * {@link #initForMenu(Context, Menu)}. +     * +     * @param root Intended parent of the MenuView. +     * @return A freshly created MenuView. +     */ +    public MenuView getMenuView(ViewGroup root); + +    /** +     * Update the menu UI in response to a change. Called by +     * MenuBuilder during the normal course of operation. +     * +     * @param cleared true if the menu was entirely cleared +     */ +    public void updateMenuView(boolean cleared); + +    /** +     * Set a callback object that will be notified of menu events +     * related to this specific presentation. +     * @param cb Callback that will be notified of future events +     */ +    public void setCallback(Callback cb); + +    /** +     * Called by Menu implementations to indicate that a submenu item +     * has been selected. An active Callback should be notified, and +     * if applicable the presenter should present the submenu. +     * +     * @param subMenu SubMenu being opened +     * @return true if the the event was handled, false otherwise. +     */ +    public boolean onSubMenuSelected(SubMenuBuilder subMenu); + +    /** +     * Called by Menu implementations to indicate that a menu or submenu is +     * closing. Presenter implementations should close the representation +     * of the menu indicated as necessary and notify a registered callback. +     * +     * @param menu Menu or submenu that is closing. +     * @param allMenusAreClosing True if all associated menus are closing. +     */ +    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing); + +    /** +     * Called by Menu implementations to flag items that will be shown as actions. +     * @return true if this presenter changed the action status of any items. +     */ +    public boolean flagActionItems(); + +    /** +     * Called when a menu item with a collapsable action view should expand its action view. +     * +     * @param menu Menu containing the item to be expanded +     * @param item Item to be expanded +     * @return true if this presenter expanded the action view, false otherwise. +     */ +    public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item); + +    /** +     * Called when a menu item with a collapsable action view should collapse its action view. +     * +     * @param menu Menu containing the item to be collapsed +     * @param item Item to be collapsed +     * @return true if this presenter collapsed the action view, false otherwise. +     */ +    public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item); + +    /** +     * Returns an ID for determining how to save/restore instance state. +     * @return a valid ID value. +     */ +    public int getId(); + +    /** +     * Returns a Parcelable describing the current state of the presenter. +     * It will be passed to the {@link #onRestoreInstanceState(Parcelable)} +     * method of the presenter sharing the same ID later. +     * @return The saved instance state +     */ +    public Parcelable onSaveInstanceState(); + +    /** +     * Supplies the previously saved instance state to be restored. +     * @param state The previously saved instance state +     */ +    public void onRestoreInstanceState(Parcelable state); +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuView.java new file mode 100644 index 000000000..323ba2d88 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuView.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2006 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.actionbarsherlock.internal.view.menu; + +import android.graphics.drawable.Drawable; + +/** + * Minimal interface for a menu view.  {@link #initialize(MenuBuilder)} must be called for the + * menu to be functional. + * + * @hide + */ +public interface MenuView { +    /** +     * Initializes the menu to the given menu. This should be called after the +     * view is inflated. +     * +     * @param menu The menu that this MenuView should display. +     */ +    public void initialize(MenuBuilder menu); + +    /** +     * Returns the default animations to be used for this menu when entering/exiting. +     * @return A resource ID for the default animations to be used for this menu. +     */ +    public int getWindowAnimations(); + +    /** +     * Minimal interface for a menu item view.  {@link #initialize(MenuItemImpl, int)} must be called +     * for the item to be functional. +     */ +    public interface ItemView { +        /** +         * Initializes with the provided MenuItemData.  This should be called after the view is +         * inflated. +         * @param itemData The item that this ItemView should display. +         * @param menuType The type of this menu, one of +         *            {@link MenuBuilder#TYPE_ICON}, {@link MenuBuilder#TYPE_EXPANDED}, +         *            {@link MenuBuilder#TYPE_DIALOG}). +         */ +        public void initialize(MenuItemImpl itemData, int menuType); + +        /** +         * Gets the item data that this view is displaying. +         * @return the item data, or null if there is not one +         */ +        public MenuItemImpl getItemData(); + +        /** +         * Sets the title of the item view. +         * @param title The title to set. +         */ +        public void setTitle(CharSequence title); + +        /** +         * Sets the enabled state of the item view. +         * @param enabled Whether the item view should be enabled. +         */ +        public void setEnabled(boolean enabled); + +        /** +         * Displays the checkbox for the item view.  This does not ensure the item view will be +         * checked, for that use {@link #setChecked}. +         * @param checkable Whether to display the checkbox or to hide it +         */ +        public void setCheckable(boolean checkable); + +        /** +         * Checks the checkbox for the item view.  If the checkbox is hidden, it will NOT be +         * made visible, call {@link #setCheckable(boolean)} for that. +         * @param checked Whether the checkbox should be checked +         */ +        public void setChecked(boolean checked); + +        /** +         * Sets the shortcut for the item. +         * @param showShortcut Whether a shortcut should be shown(if false, the value of +         * shortcutKey should be ignored). +         * @param shortcutKey The shortcut key that should be shown on the ItemView. +         */ +        public void setShortcut(boolean showShortcut, char shortcutKey); + +        /** +         * Set the icon of this item view. +         * @param icon The icon of this item. null to hide the icon. +         */ +        public void setIcon(Drawable icon); + +        /** +         * Whether this item view prefers displaying the condensed title rather +         * than the normal title. If a condensed title is not available, the +         * normal title will be used. +         * +         * @return Whether this item view prefers displaying the condensed +         *         title. +         */ +        public boolean prefersCondensedTitle(); + +        /** +         * Whether this item view shows an icon. +         * +         * @return Whether this item view shows an icon. +         */ +        public boolean showsIcon(); +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java new file mode 100644 index 000000000..3d4dd42fd --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java @@ -0,0 +1,185 @@ +package com.actionbarsherlock.internal.view.menu; + +import java.util.WeakHashMap; +import android.content.ComponentName; +import android.content.Intent; +import android.view.KeyEvent; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.SubMenu; + +public class MenuWrapper implements Menu { +    private final android.view.Menu mNativeMenu; + +    private final WeakHashMap<android.view.MenuItem, MenuItem> mNativeMap = +            new WeakHashMap<android.view.MenuItem, MenuItem>(); + + +    public MenuWrapper(android.view.Menu nativeMenu) { +        mNativeMenu = nativeMenu; +    } + +    public android.view.Menu unwrap() { +        return mNativeMenu; +    } + +    private MenuItem addInternal(android.view.MenuItem nativeItem) { +        MenuItem item = new MenuItemWrapper(nativeItem); +        mNativeMap.put(nativeItem, item); +        return item; +    } + +    @Override +    public MenuItem add(CharSequence title) { +        return addInternal(mNativeMenu.add(title)); +    } + +    @Override +    public MenuItem add(int titleRes) { +        return addInternal(mNativeMenu.add(titleRes)); +    } + +    @Override +    public MenuItem add(int groupId, int itemId, int order, CharSequence title) { +        return addInternal(mNativeMenu.add(groupId, itemId, order, title)); +    } + +    @Override +    public MenuItem add(int groupId, int itemId, int order, int titleRes) { +        return addInternal(mNativeMenu.add(groupId, itemId, order, titleRes)); +    } + +    private SubMenu addInternal(android.view.SubMenu nativeSubMenu) { +        SubMenu subMenu = new SubMenuWrapper(nativeSubMenu); +        android.view.MenuItem nativeItem = nativeSubMenu.getItem(); +        MenuItem item = subMenu.getItem(); +        mNativeMap.put(nativeItem, item); +        return subMenu; +    } + +    @Override +    public SubMenu addSubMenu(CharSequence title) { +        return addInternal(mNativeMenu.addSubMenu(title)); +    } + +    @Override +    public SubMenu addSubMenu(int titleRes) { +        return addInternal(mNativeMenu.addSubMenu(titleRes)); +    } + +    @Override +    public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) { +        return addInternal(mNativeMenu.addSubMenu(groupId, itemId, order, title)); +    } + +    @Override +    public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) { +        return addInternal(mNativeMenu.addSubMenu(groupId, itemId, order, titleRes)); +    } + +    @Override +    public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) { +        int result; +        if (outSpecificItems != null) { +            android.view.MenuItem[] nativeOutItems = new android.view.MenuItem[outSpecificItems.length]; +            result = mNativeMenu.addIntentOptions(groupId, itemId, order, caller, specifics, intent, flags, nativeOutItems); +            for (int i = 0, length = outSpecificItems.length; i < length; i++) { +                outSpecificItems[i] = new MenuItemWrapper(nativeOutItems[i]); +            } +        } else { +            result = mNativeMenu.addIntentOptions(groupId, itemId, order, caller, specifics, intent, flags, null); +        } +        return result; +    } + +    @Override +    public void removeItem(int id) { +        mNativeMenu.removeItem(id); +    } + +    @Override +    public void removeGroup(int groupId) { +        mNativeMenu.removeGroup(groupId); +    } + +    @Override +    public void clear() { +        mNativeMap.clear(); +        mNativeMenu.clear(); +    } + +    @Override +    public void setGroupCheckable(int group, boolean checkable, boolean exclusive) { +        mNativeMenu.setGroupCheckable(group, checkable, exclusive); +    } + +    @Override +    public void setGroupVisible(int group, boolean visible) { +        mNativeMenu.setGroupVisible(group, visible); +    } + +    @Override +    public void setGroupEnabled(int group, boolean enabled) { +        mNativeMenu.setGroupEnabled(group, enabled); +    } + +    @Override +    public boolean hasVisibleItems() { +        return mNativeMenu.hasVisibleItems(); +    } + +    @Override +    public MenuItem findItem(int id) { +        android.view.MenuItem nativeItem = mNativeMenu.findItem(id); +        return findItem(nativeItem); +    } + +    public MenuItem findItem(android.view.MenuItem nativeItem) { +        if (nativeItem == null) { +            return null; +        } + +        MenuItem wrapped = mNativeMap.get(nativeItem); +        if (wrapped != null) { +            return wrapped; +        } + +        return addInternal(nativeItem); +    } + +    @Override +    public int size() { +        return mNativeMenu.size(); +    } + +    @Override +    public MenuItem getItem(int index) { +        android.view.MenuItem nativeItem = mNativeMenu.getItem(index); +        return findItem(nativeItem); +    } + +    @Override +    public void close() { +        mNativeMenu.close(); +    } + +    @Override +    public boolean performShortcut(int keyCode, KeyEvent event, int flags) { +        return mNativeMenu.performShortcut(keyCode, event, flags); +    } + +    @Override +    public boolean isShortcutKey(int keyCode, KeyEvent event) { +        return mNativeMenu.isShortcutKey(keyCode, event); +    } + +    @Override +    public boolean performIdentifierAction(int id, int flags) { +        return mNativeMenu.performIdentifierAction(id, flags); +    } + +    @Override +    public void setQwertyMode(boolean isQwerty) { +        mNativeMenu.setQwertyMode(isQwerty); +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java new file mode 100644 index 000000000..6679cf386 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2006 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.actionbarsherlock.internal.view.menu; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.View; + +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.SubMenu; + +/** + * The model for a sub menu, which is an extension of the menu.  Most methods are proxied to + * the parent menu. + */ +public class SubMenuBuilder extends MenuBuilder implements SubMenu { +    private MenuBuilder mParentMenu; +    private MenuItemImpl mItem; + +    public SubMenuBuilder(Context context, MenuBuilder parentMenu, MenuItemImpl item) { +        super(context); + +        mParentMenu = parentMenu; +        mItem = item; +    } + +    @Override +    public void setQwertyMode(boolean isQwerty) { +        mParentMenu.setQwertyMode(isQwerty); +    } + +    @Override +    public boolean isQwertyMode() { +        return mParentMenu.isQwertyMode(); +    } + +    @Override +    public void setShortcutsVisible(boolean shortcutsVisible) { +        mParentMenu.setShortcutsVisible(shortcutsVisible); +    } + +    @Override +    public boolean isShortcutsVisible() { +        return mParentMenu.isShortcutsVisible(); +    } + +    public Menu getParentMenu() { +        return mParentMenu; +    } + +    public MenuItem getItem() { +        return mItem; +    } + +    @Override +    public void setCallback(Callback callback) { +        mParentMenu.setCallback(callback); +    } + +    @Override +    public MenuBuilder getRootMenu() { +        return mParentMenu; +    } + +    @Override +    boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) { +        return super.dispatchMenuItemSelected(menu, item) || +                mParentMenu.dispatchMenuItemSelected(menu, item); +    } + +    public SubMenu setIcon(Drawable icon) { +        mItem.setIcon(icon); +        return this; +    } + +    public SubMenu setIcon(int iconRes) { +        mItem.setIcon(iconRes); +        return this; +    } + +    public SubMenu setHeaderIcon(Drawable icon) { +        return (SubMenu) super.setHeaderIconInt(icon); +    } + +    public SubMenu setHeaderIcon(int iconRes) { +        return (SubMenu) super.setHeaderIconInt(iconRes); +    } + +    public SubMenu setHeaderTitle(CharSequence title) { +        return (SubMenu) super.setHeaderTitleInt(title); +    } + +    public SubMenu setHeaderTitle(int titleRes) { +        return (SubMenu) super.setHeaderTitleInt(titleRes); +    } + +    public SubMenu setHeaderView(View view) { +        return (SubMenu) super.setHeaderViewInt(view); +    } + +    @Override +    public boolean expandItemActionView(MenuItemImpl item) { +        return mParentMenu.expandItemActionView(item); +    } + +    @Override +    public boolean collapseItemActionView(MenuItemImpl item) { +        return mParentMenu.collapseItemActionView(item); +    } + +    @Override +    public String getActionViewStatesKey() { +        final int itemId = mItem != null ? mItem.getItemId() : 0; +        if (itemId == 0) { +            return null; +        } +        return super.getActionViewStatesKey() + ":" + itemId; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java new file mode 100644 index 000000000..7d307acb1 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java @@ -0,0 +1,72 @@ +package com.actionbarsherlock.internal.view.menu; + +import android.graphics.drawable.Drawable; +import android.view.View; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.SubMenu; + +public class SubMenuWrapper extends MenuWrapper implements SubMenu { +    private final android.view.SubMenu mNativeSubMenu; +    private MenuItem mItem = null; + +    public SubMenuWrapper(android.view.SubMenu nativeSubMenu) { +        super(nativeSubMenu); +        mNativeSubMenu = nativeSubMenu; +    } + + +    @Override +    public SubMenu setHeaderTitle(int titleRes) { +        mNativeSubMenu.setHeaderTitle(titleRes); +        return this; +    } + +    @Override +    public SubMenu setHeaderTitle(CharSequence title) { +        mNativeSubMenu.setHeaderTitle(title); +        return this; +    } + +    @Override +    public SubMenu setHeaderIcon(int iconRes) { +        mNativeSubMenu.setHeaderIcon(iconRes); +        return this; +    } + +    @Override +    public SubMenu setHeaderIcon(Drawable icon) { +        mNativeSubMenu.setHeaderIcon(icon); +        return this; +    } + +    @Override +    public SubMenu setHeaderView(View view) { +        mNativeSubMenu.setHeaderView(view); +        return this; +    } + +    @Override +    public void clearHeader() { +        mNativeSubMenu.clearHeader(); +    } + +    @Override +    public SubMenu setIcon(int iconRes) { +        mNativeSubMenu.setIcon(iconRes); +        return this; +    } + +    @Override +    public SubMenu setIcon(Drawable icon) { +        mNativeSubMenu.setIcon(icon); +        return this; +    } + +    @Override +    public MenuItem getItem() { +        if (mItem == null) { +            mItem = new MenuItemWrapper(mNativeSubMenu.getItem()); +        } +        return mItem; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/AbsActionBarView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/AbsActionBarView.java new file mode 100644 index 000000000..3a4a44675 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/AbsActionBarView.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.actionbarsherlock.internal.widget; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.os.Build; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; + +import com.actionbarsherlock.R; +import com.actionbarsherlock.internal.nineoldandroids.animation.Animator; +import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorSet; +import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator; +import com.actionbarsherlock.internal.nineoldandroids.view.NineViewGroup; +import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter; +import com.actionbarsherlock.internal.view.menu.ActionMenuView; + +import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean; + +public abstract class AbsActionBarView extends NineViewGroup { +    protected ActionMenuView mMenuView; +    protected ActionMenuPresenter mActionMenuPresenter; +    protected ActionBarContainer mSplitView; +    protected boolean mSplitActionBar; +    protected boolean mSplitWhenNarrow; +    protected int mContentHeight; + +    final Context mContext; + +    protected Animator mVisibilityAnim; +    protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener(); + +    private static final /*Time*/Interpolator sAlphaInterpolator = new DecelerateInterpolator(); + +    private static final int FADE_DURATION = 200; + +    public AbsActionBarView(Context context) { +        super(context); +        mContext = context; +    } + +    public AbsActionBarView(Context context, AttributeSet attrs) { +        super(context, attrs); +        mContext = context; +    } + +    public AbsActionBarView(Context context, AttributeSet attrs, int defStyle) { +        super(context, attrs, defStyle); +        mContext = context; +    } + +    /* +     * Must be public so we can dispatch pre-2.2 via ActionBarImpl. +     */ +    @Override +    public void onConfigurationChanged(Configuration newConfig) { +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { +            super.onConfigurationChanged(newConfig); +        } else if (mMenuView != null) { +            mMenuView.onConfigurationChanged(newConfig); +        } + +        // Action bar can change size on configuration changes. +        // Reread the desired height from the theme-specified style. +        TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar, +                R.attr.actionBarStyle, 0); +        setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0)); +        a.recycle(); +        if (mSplitWhenNarrow) { +            setSplitActionBar(getResources_getBoolean(getContext(), +                    R.bool.abs__split_action_bar_is_narrow)); +        } +        if (mActionMenuPresenter != null) { +            mActionMenuPresenter.onConfigurationChanged(newConfig); +        } +    } + +    /** +     * Sets whether the bar should be split right now, no questions asked. +     * @param split true if the bar should split +     */ +    public void setSplitActionBar(boolean split) { +        mSplitActionBar = split; +    } + +    /** +     * Sets whether the bar should split if we enter a narrow screen configuration. +     * @param splitWhenNarrow true if the bar should check to split after a config change +     */ +    public void setSplitWhenNarrow(boolean splitWhenNarrow) { +        mSplitWhenNarrow = splitWhenNarrow; +    } + +    public void setContentHeight(int height) { +        mContentHeight = height; +        requestLayout(); +    } + +    public int getContentHeight() { +        return mContentHeight; +    } + +    public void setSplitView(ActionBarContainer splitView) { +        mSplitView = splitView; +    } + +    /** +     * @return Current visibility or if animating, the visibility being animated to. +     */ +    public int getAnimatedVisibility() { +        if (mVisibilityAnim != null) { +            return mVisAnimListener.mFinalVisibility; +        } +        return getVisibility(); +    } + +    public void animateToVisibility(int visibility) { +        if (mVisibilityAnim != null) { +            mVisibilityAnim.cancel(); +        } +        if (visibility == VISIBLE) { +            if (getVisibility() != VISIBLE) { +                setAlpha(0); +                if (mSplitView != null && mMenuView != null) { +                    mMenuView.setAlpha(0); +                } +            } +            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1); +            anim.setDuration(FADE_DURATION); +            anim.setInterpolator(sAlphaInterpolator); +            if (mSplitView != null && mMenuView != null) { +                AnimatorSet set = new AnimatorSet(); +                ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 1); +                splitAnim.setDuration(FADE_DURATION); +                set.addListener(mVisAnimListener.withFinalVisibility(visibility)); +                set.play(anim).with(splitAnim); +                set.start(); +            } else { +                anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); +                anim.start(); +            } +        } else { +            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0); +            anim.setDuration(FADE_DURATION); +            anim.setInterpolator(sAlphaInterpolator); +            if (mSplitView != null && mMenuView != null) { +                AnimatorSet set = new AnimatorSet(); +                ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 0); +                splitAnim.setDuration(FADE_DURATION); +                set.addListener(mVisAnimListener.withFinalVisibility(visibility)); +                set.play(anim).with(splitAnim); +                set.start(); +            } else { +                anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); +                anim.start(); +            } +        } +    } + +    @Override +    public void setVisibility(int visibility) { +        if (mVisibilityAnim != null) { +            mVisibilityAnim.end(); +        } +        super.setVisibility(visibility); +    } + +    public boolean showOverflowMenu() { +        if (mActionMenuPresenter != null) { +            return mActionMenuPresenter.showOverflowMenu(); +        } +        return false; +    } + +    public void postShowOverflowMenu() { +        post(new Runnable() { +            public void run() { +                showOverflowMenu(); +            } +        }); +    } + +    public boolean hideOverflowMenu() { +        if (mActionMenuPresenter != null) { +            return mActionMenuPresenter.hideOverflowMenu(); +        } +        return false; +    } + +    public boolean isOverflowMenuShowing() { +        if (mActionMenuPresenter != null) { +            return mActionMenuPresenter.isOverflowMenuShowing(); +        } +        return false; +    } + +    public boolean isOverflowReserved() { +        return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved(); +    } + +    public void dismissPopupMenus() { +        if (mActionMenuPresenter != null) { +            mActionMenuPresenter.dismissPopupMenus(); +        } +    } + +    protected int measureChildView(View child, int availableWidth, int childSpecHeight, +            int spacing) { +        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), +                childSpecHeight); + +        availableWidth -= child.getMeasuredWidth(); +        availableWidth -= spacing; + +        return Math.max(0, availableWidth); +    } + +    protected int positionChild(View child, int x, int y, int contentHeight) { +        int childWidth = child.getMeasuredWidth(); +        int childHeight = child.getMeasuredHeight(); +        int childTop = y + (contentHeight - childHeight) / 2; + +        child.layout(x, childTop, x + childWidth, childTop + childHeight); + +        return childWidth; +    } + +    protected int positionChildInverse(View child, int x, int y, int contentHeight) { +        int childWidth = child.getMeasuredWidth(); +        int childHeight = child.getMeasuredHeight(); +        int childTop = y + (contentHeight - childHeight) / 2; + +        child.layout(x - childWidth, childTop, x, childTop + childHeight); + +        return childWidth; +    } + +    protected class VisibilityAnimListener implements Animator.AnimatorListener { +        private boolean mCanceled = false; +        int mFinalVisibility; + +        public VisibilityAnimListener withFinalVisibility(int visibility) { +            mFinalVisibility = visibility; +            return this; +        } + +        @Override +        public void onAnimationStart(Animator animation) { +            setVisibility(VISIBLE); +            mVisibilityAnim = animation; +            mCanceled = false; +        } + +        @Override +        public void onAnimationEnd(Animator animation) { +            if (mCanceled) return; + +            mVisibilityAnim = null; +            setVisibility(mFinalVisibility); +            if (mSplitView != null && mMenuView != null) { +                mMenuView.setVisibility(mFinalVisibility); +            } +        } + +        @Override +        public void onAnimationCancel(Animator animation) { +            mCanceled = true; +        } + +        @Override +        public void onAnimationRepeat(Animator animation) { +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java new file mode 100644 index 000000000..1d9c68b37 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import com.actionbarsherlock.R; +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.internal.nineoldandroids.widget.NineFrameLayout; + +/** + * This class acts as a container for the action bar view and action mode context views. + * It applies special styles as needed to help handle animated transitions between them. + * @hide + */ +public class ActionBarContainer extends NineFrameLayout { +    private boolean mIsTransitioning; +    private View mTabContainer; +    private ActionBarView mActionBarView; + +    private Drawable mBackground; +    private Drawable mStackedBackground; +    private Drawable mSplitBackground; +    private boolean mIsSplit; +    private boolean mIsStacked; + +    public ActionBarContainer(Context context) { +        this(context, null); +    } + +    public ActionBarContainer(Context context, AttributeSet attrs) { +        super(context, attrs); + +        setBackgroundDrawable(null); + +        TypedArray a = context.obtainStyledAttributes(attrs, +                R.styleable.SherlockActionBar); +        mBackground = a.getDrawable(R.styleable.SherlockActionBar_background); +        mStackedBackground = a.getDrawable( +                R.styleable.SherlockActionBar_backgroundStacked); + +        //Fix for issue #379 +        if (mStackedBackground instanceof ColorDrawable && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { +            Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); +            Canvas c = new Canvas(bitmap); +            mStackedBackground.draw(c); +            int color = bitmap.getPixel(0, 0); +            bitmap.recycle(); +            mStackedBackground = new IcsColorDrawable(color); +        } + +        if (getId() == R.id.abs__split_action_bar) { +            mIsSplit = true; +            mSplitBackground = a.getDrawable( +                    R.styleable.SherlockActionBar_backgroundSplit); +        } +        a.recycle(); + +        setWillNotDraw(mIsSplit ? mSplitBackground == null : +                mBackground == null && mStackedBackground == null); +    } + +    @Override +    public void onFinishInflate() { +        super.onFinishInflate(); +        mActionBarView = (ActionBarView) findViewById(R.id.abs__action_bar); +    } + +    public void setPrimaryBackground(Drawable bg) { +        mBackground = bg; +        invalidate(); +    } + +    public void setStackedBackground(Drawable bg) { +        mStackedBackground = bg; +        invalidate(); +    } + +    public void setSplitBackground(Drawable bg) { +        mSplitBackground = bg; +        invalidate(); +    } + +    /** +     * Set the action bar into a "transitioning" state. While transitioning +     * the bar will block focus and touch from all of its descendants. This +     * prevents the user from interacting with the bar while it is animating +     * in or out. +     * +     * @param isTransitioning true if the bar is currently transitioning, false otherwise. +     */ +    public void setTransitioning(boolean isTransitioning) { +        mIsTransitioning = isTransitioning; +        setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS +                : FOCUS_AFTER_DESCENDANTS); +    } + +    @Override +    public boolean onInterceptTouchEvent(MotionEvent ev) { +        return mIsTransitioning || super.onInterceptTouchEvent(ev); +    } + +    @Override +    public boolean onTouchEvent(MotionEvent ev) { +        super.onTouchEvent(ev); + +        // An action bar always eats touch events. +        return true; +    } + +    @Override +    public boolean onHoverEvent(MotionEvent ev) { +        super.onHoverEvent(ev); + +        // An action bar always eats hover events. +        return true; +    } + +    public void setTabContainer(ScrollingTabContainerView tabView) { +        if (mTabContainer != null) { +            removeView(mTabContainer); +        } +        mTabContainer = tabView; +        if (tabView != null) { +            addView(tabView); +            final ViewGroup.LayoutParams lp = tabView.getLayoutParams(); +            lp.width = LayoutParams.MATCH_PARENT; +            lp.height = LayoutParams.WRAP_CONTENT; +            tabView.setAllowCollapse(false); +        } +    } + +    public View getTabContainer() { +        return mTabContainer; +    } + +    @Override +    public void onDraw(Canvas canvas) { +        if (getWidth() == 0 || getHeight() == 0) { +            return; +        } + +        if (mIsSplit) { +            if (mSplitBackground != null) mSplitBackground.draw(canvas); +        } else { +            if (mBackground != null) { +                mBackground.draw(canvas); +            } +            if (mStackedBackground != null && mIsStacked) { +                mStackedBackground.draw(canvas); +            } +        } +    } + +    //This causes the animation reflection to fail on pre-HC platforms +    //@Override +    //public android.view.ActionMode startActionModeForChild(View child, android.view.ActionMode.Callback callback) { +    //    // No starting an action mode for an action bar child! (Where would it go?) +    //    return null; +    //} + +    @Override +    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        super.onMeasure(widthMeasureSpec, heightMeasureSpec); + +        if (mActionBarView == null) return; + +        final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams(); +        final int actionBarViewHeight = mActionBarView.isCollapsed() ? 0 : +                mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; + +        if (mTabContainer != null && mTabContainer.getVisibility() != GONE) { +            final int mode = MeasureSpec.getMode(heightMeasureSpec); +            if (mode == MeasureSpec.AT_MOST) { +                final int maxHeight = MeasureSpec.getSize(heightMeasureSpec); +                setMeasuredDimension(getMeasuredWidth(), +                        Math.min(actionBarViewHeight + mTabContainer.getMeasuredHeight(), +                                maxHeight)); +            } +        } +    } + +    @Override +    public void onLayout(boolean changed, int l, int t, int r, int b) { +        super.onLayout(changed, l, t, r, b); + +        final boolean hasTabs = mTabContainer != null && mTabContainer.getVisibility() != GONE; + +        if (mTabContainer != null && mTabContainer.getVisibility() != GONE) { +            final int containerHeight = getMeasuredHeight(); +            final int tabHeight = mTabContainer.getMeasuredHeight(); + +            if ((mActionBarView.getDisplayOptions() & ActionBar.DISPLAY_SHOW_HOME) == 0) { +                // Not showing home, put tabs on top. +                final int count = getChildCount(); +                for (int i = 0; i < count; i++) { +                    final View child = getChildAt(i); + +                    if (child == mTabContainer) continue; + +                    if (!mActionBarView.isCollapsed()) { +                        child.offsetTopAndBottom(tabHeight); +                    } +                } +                mTabContainer.layout(l, 0, r, tabHeight); +            } else { +                mTabContainer.layout(l, containerHeight - tabHeight, r, containerHeight); +            } +        } + +        boolean needsInvalidate = false; +        if (mIsSplit) { +            if (mSplitBackground != null) { +                mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); +                needsInvalidate = true; +            } +        } else { +            if (mBackground != null) { +                mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(), +                        mActionBarView.getRight(), mActionBarView.getBottom()); +                needsInvalidate = true; +            } +            if ((mIsStacked = hasTabs && mStackedBackground != null)) { +                mStackedBackground.setBounds(mTabContainer.getLeft(), mTabContainer.getTop(), +                        mTabContainer.getRight(), mTabContainer.getBottom()); +                needsInvalidate = true; +            } +        } + +        if (needsInvalidate) { +            invalidate(); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContextView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContextView.java new file mode 100644 index 000000000..9ec250f38 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContextView.java @@ -0,0 +1,518 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; +import android.view.animation.DecelerateInterpolator; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.actionbarsherlock.R; +import com.actionbarsherlock.internal.nineoldandroids.animation.Animator; +import com.actionbarsherlock.internal.nineoldandroids.animation.Animator.AnimatorListener; +import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorSet; +import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator; +import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy; +import com.actionbarsherlock.internal.nineoldandroids.widget.NineLinearLayout; +import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter; +import com.actionbarsherlock.internal.view.menu.ActionMenuView; +import com.actionbarsherlock.internal.view.menu.MenuBuilder; +import com.actionbarsherlock.view.ActionMode; + +/** + * @hide + */ +public class ActionBarContextView extends AbsActionBarView implements AnimatorListener { +    //UNUSED private static final String TAG = "ActionBarContextView"; + +    private CharSequence mTitle; +    private CharSequence mSubtitle; + +    private NineLinearLayout mClose; +    private View mCustomView; +    private LinearLayout mTitleLayout; +    private TextView mTitleView; +    private TextView mSubtitleView; +    private int mTitleStyleRes; +    private int mSubtitleStyleRes; +    private Drawable mSplitBackground; + +    private Animator mCurrentAnimation; +    private boolean mAnimateInOnLayout; +    private int mAnimationMode; + +    private static final int ANIMATE_IDLE = 0; +    private static final int ANIMATE_IN = 1; +    private static final int ANIMATE_OUT = 2; + +    public ActionBarContextView(Context context) { +        this(context, null); +    } + +    public ActionBarContextView(Context context, AttributeSet attrs) { +        this(context, attrs, R.attr.actionModeStyle); +    } + +    public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) { +        super(context, attrs, defStyle); + +        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SherlockActionMode, defStyle, 0); +        setBackgroundDrawable(a.getDrawable( +                R.styleable.SherlockActionMode_background)); +        mTitleStyleRes = a.getResourceId( +                R.styleable.SherlockActionMode_titleTextStyle, 0); +        mSubtitleStyleRes = a.getResourceId( +                R.styleable.SherlockActionMode_subtitleTextStyle, 0); + +        mContentHeight = a.getLayoutDimension( +                R.styleable.SherlockActionMode_height, 0); + +        mSplitBackground = a.getDrawable( +                R.styleable.SherlockActionMode_backgroundSplit); + +        a.recycle(); +    } + +    @Override +    public void onDetachedFromWindow() { +        super.onDetachedFromWindow(); +        if (mActionMenuPresenter != null) { +            mActionMenuPresenter.hideOverflowMenu(); +            mActionMenuPresenter.hideSubMenus(); +        } +    } + +    @Override +    public void setSplitActionBar(boolean split) { +        if (mSplitActionBar != split) { +            if (mActionMenuPresenter != null) { +                // Mode is already active; move everything over and adjust the menu itself. +                final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, +                        LayoutParams.MATCH_PARENT); +                if (!split) { +                    mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); +                    mMenuView.setBackgroundDrawable(null); +                    final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); +                    if (oldParent != null) oldParent.removeView(mMenuView); +                    addView(mMenuView, layoutParams); +                } else { +                    // Allow full screen width in split mode. +                    mActionMenuPresenter.setWidthLimit( +                            getContext().getResources().getDisplayMetrics().widthPixels, true); +                    // No limit to the item count; use whatever will fit. +                    mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); +                    // Span the whole width +                    layoutParams.width = LayoutParams.MATCH_PARENT; +                    layoutParams.height = mContentHeight; +                    mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); +                    mMenuView.setBackgroundDrawable(mSplitBackground); +                    final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); +                    if (oldParent != null) oldParent.removeView(mMenuView); +                    mSplitView.addView(mMenuView, layoutParams); +                } +            } +            super.setSplitActionBar(split); +        } +    } + +    public void setContentHeight(int height) { +        mContentHeight = height; +    } + +    public void setCustomView(View view) { +        if (mCustomView != null) { +            removeView(mCustomView); +        } +        mCustomView = view; +        if (mTitleLayout != null) { +            removeView(mTitleLayout); +            mTitleLayout = null; +        } +        if (view != null) { +            addView(view); +        } +        requestLayout(); +    } + +    public void setTitle(CharSequence title) { +        mTitle = title; +        initTitle(); +    } + +    public void setSubtitle(CharSequence subtitle) { +        mSubtitle = subtitle; +        initTitle(); +    } + +    public CharSequence getTitle() { +        return mTitle; +    } + +    public CharSequence getSubtitle() { +        return mSubtitle; +    } + +    private void initTitle() { +        if (mTitleLayout == null) { +            LayoutInflater inflater = LayoutInflater.from(getContext()); +            inflater.inflate(R.layout.abs__action_bar_title_item, this); +            mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1); +            mTitleView = (TextView) mTitleLayout.findViewById(R.id.abs__action_bar_title); +            mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.abs__action_bar_subtitle); +            if (mTitleStyleRes != 0) { +                mTitleView.setTextAppearance(mContext, mTitleStyleRes); +            } +            if (mSubtitleStyleRes != 0) { +                mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes); +            } +        } + +        mTitleView.setText(mTitle); +        mSubtitleView.setText(mSubtitle); + +        final boolean hasTitle = !TextUtils.isEmpty(mTitle); +        final boolean hasSubtitle = !TextUtils.isEmpty(mSubtitle); +        mSubtitleView.setVisibility(hasSubtitle ? VISIBLE : GONE); +        mTitleLayout.setVisibility(hasTitle || hasSubtitle ? VISIBLE : GONE); +        if (mTitleLayout.getParent() == null) { +            addView(mTitleLayout); +        } +    } + +    public void initForMode(final ActionMode mode) { +        if (mClose == null) { +            LayoutInflater inflater = LayoutInflater.from(mContext); +            mClose = (NineLinearLayout)inflater.inflate(R.layout.abs__action_mode_close_item, this, false); +            addView(mClose); +        } else if (mClose.getParent() == null) { +            addView(mClose); +        } + +        View closeButton = mClose.findViewById(R.id.abs__action_mode_close_button); +        closeButton.setOnClickListener(new OnClickListener() { +            public void onClick(View v) { +                mode.finish(); +            } +        }); + +        final MenuBuilder menu = (MenuBuilder) mode.getMenu(); +        if (mActionMenuPresenter != null) { +            mActionMenuPresenter.dismissPopupMenus(); +        } +        mActionMenuPresenter = new ActionMenuPresenter(mContext); +        mActionMenuPresenter.setReserveOverflow(true); + +        final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, +                LayoutParams.MATCH_PARENT); +        if (!mSplitActionBar) { +            menu.addMenuPresenter(mActionMenuPresenter); +            mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); +            mMenuView.setBackgroundDrawable(null); +            addView(mMenuView, layoutParams); +        } else { +            // Allow full screen width in split mode. +            mActionMenuPresenter.setWidthLimit( +                    getContext().getResources().getDisplayMetrics().widthPixels, true); +            // No limit to the item count; use whatever will fit. +            mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); +            // Span the whole width +            layoutParams.width = LayoutParams.MATCH_PARENT; +            layoutParams.height = mContentHeight; +            menu.addMenuPresenter(mActionMenuPresenter); +            mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); +            mMenuView.setBackgroundDrawable(mSplitBackground); +            mSplitView.addView(mMenuView, layoutParams); +        } + +        mAnimateInOnLayout = true; +    } + +    public void closeMode() { +        if (mAnimationMode == ANIMATE_OUT) { +            // Called again during close; just finish what we were doing. +            return; +        } +        if (mClose == null) { +            killMode(); +            return; +        } + +        finishAnimation(); +        mAnimationMode = ANIMATE_OUT; +        mCurrentAnimation = makeOutAnimation(); +        mCurrentAnimation.start(); +    } + +    private void finishAnimation() { +        final Animator a = mCurrentAnimation; +        if (a != null) { +            mCurrentAnimation = null; +            a.end(); +        } +    } + +    public void killMode() { +        finishAnimation(); +        removeAllViews(); +        if (mSplitView != null) { +            mSplitView.removeView(mMenuView); +        } +        mCustomView = null; +        mMenuView = null; +        mAnimateInOnLayout = false; +    } + +    @Override +    public boolean showOverflowMenu() { +        if (mActionMenuPresenter != null) { +            return mActionMenuPresenter.showOverflowMenu(); +        } +        return false; +    } + +    @Override +    public boolean hideOverflowMenu() { +        if (mActionMenuPresenter != null) { +            return mActionMenuPresenter.hideOverflowMenu(); +        } +        return false; +    } + +    @Override +    public boolean isOverflowMenuShowing() { +        if (mActionMenuPresenter != null) { +            return mActionMenuPresenter.isOverflowMenuShowing(); +        } +        return false; +    } + +    @Override +    protected ViewGroup.LayoutParams generateDefaultLayoutParams() { +        // Used by custom views if they don't supply layout params. Everything else +        // added to an ActionBarContextView should have them already. +        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); +    } + +    @Override +    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { +        return new MarginLayoutParams(getContext(), attrs); +    } + +    @Override +    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        final int widthMode = MeasureSpec.getMode(widthMeasureSpec); +        if (widthMode != MeasureSpec.EXACTLY) { +            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + +                    "with android:layout_width=\"match_parent\" (or fill_parent)"); +        } + +        final int heightMode = MeasureSpec.getMode(heightMeasureSpec); +        if (heightMode == MeasureSpec.UNSPECIFIED) { +            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + +                    "with android:layout_height=\"wrap_content\""); +        } + +        final int contentWidth = MeasureSpec.getSize(widthMeasureSpec); + +        int maxHeight = mContentHeight > 0 ? +                mContentHeight : MeasureSpec.getSize(heightMeasureSpec); + +        final int verticalPadding = getPaddingTop() + getPaddingBottom(); +        int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight(); +        final int height = maxHeight - verticalPadding; +        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); + +        if (mClose != null) { +            availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0); +            MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams(); +            availableWidth -= lp.leftMargin + lp.rightMargin; +        } + +        if (mMenuView != null && mMenuView.getParent() == this) { +            availableWidth = measureChildView(mMenuView, availableWidth, +                    childSpecHeight, 0); +        } + +        if (mTitleLayout != null && mCustomView == null) { +            availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0); +        } + +        if (mCustomView != null) { +            ViewGroup.LayoutParams lp = mCustomView.getLayoutParams(); +            final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ? +                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; +            final int customWidth = lp.width >= 0 ? +                    Math.min(lp.width, availableWidth) : availableWidth; +            final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ? +                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; +            final int customHeight = lp.height >= 0 ? +                    Math.min(lp.height, height) : height; +            mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode), +                    MeasureSpec.makeMeasureSpec(customHeight, customHeightMode)); +        } + +        if (mContentHeight <= 0) { +            int measuredHeight = 0; +            final int count = getChildCount(); +            for (int i = 0; i < count; i++) { +                View v = getChildAt(i); +                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding; +                if (paddedViewHeight > measuredHeight) { +                    measuredHeight = paddedViewHeight; +                } +            } +            setMeasuredDimension(contentWidth, measuredHeight); +        } else { +            setMeasuredDimension(contentWidth, maxHeight); +        } +    } + +    private Animator makeInAnimation() { +        mClose.setTranslationX(-mClose.getWidth() - +                ((MarginLayoutParams) mClose.getLayoutParams()).leftMargin); +        ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX", 0); +        buttonAnimator.setDuration(200); +        buttonAnimator.addListener(this); +        buttonAnimator.setInterpolator(new DecelerateInterpolator()); + +        AnimatorSet set = new AnimatorSet(); +        AnimatorSet.Builder b = set.play(buttonAnimator); + +        if (mMenuView != null) { +            final int count = mMenuView.getChildCount(); +            if (count > 0) { +                for (int i = count - 1, j = 0; i >= 0; i--, j++) { +                    AnimatorProxy child = AnimatorProxy.wrap(mMenuView.getChildAt(i)); +                    child.setScaleY(0); +                    ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0, 1); +                    a.setDuration(100); +                    a.setStartDelay(j * 70); +                    b.with(a); +                } +            } +        } + +        return set; +    } + +    private Animator makeOutAnimation() { +        ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX", +                -mClose.getWidth() - ((MarginLayoutParams) mClose.getLayoutParams()).leftMargin); +        buttonAnimator.setDuration(200); +        buttonAnimator.addListener(this); +        buttonAnimator.setInterpolator(new DecelerateInterpolator()); + +        AnimatorSet set = new AnimatorSet(); +        AnimatorSet.Builder b = set.play(buttonAnimator); + +        if (mMenuView != null) { +            final int count = mMenuView.getChildCount(); +            if (count > 0) { +                for (int i = 0; i < 0; i++) { +                    AnimatorProxy child = AnimatorProxy.wrap(mMenuView.getChildAt(i)); +                    child.setScaleY(0); +                    ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0); +                    a.setDuration(100); +                    a.setStartDelay(i * 70); +                    b.with(a); +                } +            } +        } + +        return set; +    } + +    @Override +    protected void onLayout(boolean changed, int l, int t, int r, int b) { +        int x = getPaddingLeft(); +        final int y = getPaddingTop(); +        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom(); + +        if (mClose != null && mClose.getVisibility() != GONE) { +            MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams(); +            x += lp.leftMargin; +            x += positionChild(mClose, x, y, contentHeight); +            x += lp.rightMargin; + +            if (mAnimateInOnLayout) { +                mAnimationMode = ANIMATE_IN; +                mCurrentAnimation = makeInAnimation(); +                mCurrentAnimation.start(); +                mAnimateInOnLayout = false; +            } +        } + +        if (mTitleLayout != null && mCustomView == null) { +            x += positionChild(mTitleLayout, x, y, contentHeight); +        } + +        if (mCustomView != null) { +            x += positionChild(mCustomView, x, y, contentHeight); +        } + +        x = r - l - getPaddingRight(); + +        if (mMenuView != null) { +            x -= positionChildInverse(mMenuView, x, y, contentHeight); +        } +    } + +    @Override +    public void onAnimationStart(Animator animation) { +    } + +    @Override +    public void onAnimationEnd(Animator animation) { +        if (mAnimationMode == ANIMATE_OUT) { +            killMode(); +        } +        mAnimationMode = ANIMATE_IDLE; +    } + +    @Override +    public void onAnimationCancel(Animator animation) { +    } + +    @Override +    public void onAnimationRepeat(Animator animation) { +    } + +    @Override +    public boolean shouldDelayChildPressedState() { +        return false; +    } + +    @Override +    public void onInitializeAccessibilityEvent(AccessibilityEvent event) { +        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { +            // Action mode started +            //TODO event.setSource(this); +            event.setClassName(getClass().getName()); +            event.setPackageName(getContext().getPackageName()); +            event.setContentDescription(mTitle); +        } else { +            //TODO super.onInitializeAccessibilityEvent(event); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarView.java new file mode 100644 index 000000000..4636de17f --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarView.java @@ -0,0 +1,1548 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.internal.widget; + +import org.xmlpull.v1.XmlPullParser; +import android.app.Activity; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.accessibility.AccessibilityEvent; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.SpinnerAdapter; +import android.widget.TextView; + +import com.actionbarsherlock.R; +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.ActionBar.OnNavigationListener; +import com.actionbarsherlock.internal.ActionBarSherlockCompat; +import com.actionbarsherlock.internal.view.menu.ActionMenuItem; +import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter; +import com.actionbarsherlock.internal.view.menu.ActionMenuView; +import com.actionbarsherlock.internal.view.menu.MenuBuilder; +import com.actionbarsherlock.internal.view.menu.MenuItemImpl; +import com.actionbarsherlock.internal.view.menu.MenuPresenter; +import com.actionbarsherlock.internal.view.menu.MenuView; +import com.actionbarsherlock.internal.view.menu.SubMenuBuilder; +import com.actionbarsherlock.view.CollapsibleActionView; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.Window; + +import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean; + +/** + * @hide + */ +public class ActionBarView extends AbsActionBarView { +    private static final String TAG = "ActionBarView"; +    private static final boolean DEBUG = false; + +    /** +     * Display options applied by default +     */ +    public static final int DISPLAY_DEFAULT = 0; + +    /** +     * Display options that require re-layout as opposed to a simple invalidate +     */ +    private static final int DISPLAY_RELAYOUT_MASK = +            ActionBar.DISPLAY_SHOW_HOME | +            ActionBar.DISPLAY_USE_LOGO | +            ActionBar.DISPLAY_HOME_AS_UP | +            ActionBar.DISPLAY_SHOW_CUSTOM | +            ActionBar.DISPLAY_SHOW_TITLE; + +    private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.LEFT | Gravity.CENTER_VERTICAL; + +    private int mNavigationMode; +    private int mDisplayOptions = -1; +    private CharSequence mTitle; +    private CharSequence mSubtitle; +    private Drawable mIcon; +    private Drawable mLogo; + +    private HomeView mHomeLayout; +    private HomeView mExpandedHomeLayout; +    private LinearLayout mTitleLayout; +    private TextView mTitleView; +    private TextView mSubtitleView; +    private View mTitleUpView; + +    private IcsSpinner mSpinner; +    private IcsLinearLayout mListNavLayout; +    private ScrollingTabContainerView mTabScrollView; +    private View mCustomNavView; +    private IcsProgressBar mProgressView; +    private IcsProgressBar mIndeterminateProgressView; + +    private int mProgressBarPadding; +    private int mItemPadding; + +    private int mTitleStyleRes; +    private int mSubtitleStyleRes; +    private int mProgressStyle; +    private int mIndeterminateProgressStyle; + +    private boolean mUserTitle; +    private boolean mIncludeTabs; +    private boolean mIsCollapsable; +    private boolean mIsCollapsed; + +    private MenuBuilder mOptionsMenu; + +    private ActionBarContextView mContextView; + +    private ActionMenuItem mLogoNavItem; + +    private SpinnerAdapter mSpinnerAdapter; +    private OnNavigationListener mCallback; + +    //UNUSED private Runnable mTabSelector; + +    private ExpandedActionViewMenuPresenter mExpandedMenuPresenter; +    View mExpandedActionView; + +    Window.Callback mWindowCallback; + +    @SuppressWarnings("rawtypes") +    private final IcsAdapterView.OnItemSelectedListener mNavItemSelectedListener = +            new IcsAdapterView.OnItemSelectedListener() { +        public void onItemSelected(IcsAdapterView parent, View view, int position, long id) { +            if (mCallback != null) { +                mCallback.onNavigationItemSelected(position, id); +            } +        } +        public void onNothingSelected(IcsAdapterView parent) { +            // Do nothing +        } +    }; + +    private final OnClickListener mExpandedActionViewUpListener = new OnClickListener() { +        @Override +        public void onClick(View v) { +            final MenuItemImpl item = mExpandedMenuPresenter.mCurrentExpandedItem; +            if (item != null) { +                item.collapseActionView(); +            } +        } +    }; + +    private final OnClickListener mUpClickListener = new OnClickListener() { +        public void onClick(View v) { +            mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem); +        } +    }; + +    public ActionBarView(Context context, AttributeSet attrs) { +        super(context, attrs); + +        // Background is always provided by the container. +        setBackgroundResource(0); + +        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SherlockActionBar, +                R.attr.actionBarStyle, 0); + +        ApplicationInfo appInfo = context.getApplicationInfo(); +        PackageManager pm = context.getPackageManager(); +        mNavigationMode = a.getInt(R.styleable.SherlockActionBar_navigationMode, +                ActionBar.NAVIGATION_MODE_STANDARD); +        mTitle = a.getText(R.styleable.SherlockActionBar_title); +        mSubtitle = a.getText(R.styleable.SherlockActionBar_subtitle); + +        mLogo = a.getDrawable(R.styleable.SherlockActionBar_logo); +        if (mLogo == null) { +            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { +                if (context instanceof Activity) { +                    //Even though native methods existed in API 9 and 10 they don't work +                    //so just parse the manifest to look for the logo pre-Honeycomb +                    final int resId = loadLogoFromManifest((Activity) context); +                    if (resId != 0) { +                        mLogo = context.getResources().getDrawable(resId); +                    } +                } +            } else { +                if (context instanceof Activity) { +                    try { +                        mLogo = pm.getActivityLogo(((Activity) context).getComponentName()); +                    } catch (NameNotFoundException e) { +                        Log.e(TAG, "Activity component name not found!", e); +                    } +                } +                if (mLogo == null) { +                    mLogo = appInfo.loadLogo(pm); +                } +            } +        } + +        mIcon = a.getDrawable(R.styleable.SherlockActionBar_icon); +        if (mIcon == null) { +            if (context instanceof Activity) { +                try { +                    mIcon = pm.getActivityIcon(((Activity) context).getComponentName()); +                } catch (NameNotFoundException e) { +                    Log.e(TAG, "Activity component name not found!", e); +                } +            } +            if (mIcon == null) { +                mIcon = appInfo.loadIcon(pm); +            } +        } + +        final LayoutInflater inflater = LayoutInflater.from(context); + +        final int homeResId = a.getResourceId( +                R.styleable.SherlockActionBar_homeLayout, +                R.layout.abs__action_bar_home); + +        mHomeLayout = (HomeView) inflater.inflate(homeResId, this, false); + +        mExpandedHomeLayout = (HomeView) inflater.inflate(homeResId, this, false); +        mExpandedHomeLayout.setUp(true); +        mExpandedHomeLayout.setOnClickListener(mExpandedActionViewUpListener); +        mExpandedHomeLayout.setContentDescription(getResources().getText( +                R.string.abs__action_bar_up_description)); + +        mTitleStyleRes = a.getResourceId(R.styleable.SherlockActionBar_titleTextStyle, 0); +        mSubtitleStyleRes = a.getResourceId(R.styleable.SherlockActionBar_subtitleTextStyle, 0); +        mProgressStyle = a.getResourceId(R.styleable.SherlockActionBar_progressBarStyle, 0); +        mIndeterminateProgressStyle = a.getResourceId( +                R.styleable.SherlockActionBar_indeterminateProgressStyle, 0); + +        mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.SherlockActionBar_progressBarPadding, 0); +        mItemPadding = a.getDimensionPixelOffset(R.styleable.SherlockActionBar_itemPadding, 0); + +        setDisplayOptions(a.getInt(R.styleable.SherlockActionBar_displayOptions, DISPLAY_DEFAULT)); + +        final int customNavId = a.getResourceId(R.styleable.SherlockActionBar_customNavigationLayout, 0); +        if (customNavId != 0) { +            mCustomNavView = inflater.inflate(customNavId, this, false); +            mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD; +            setDisplayOptions(mDisplayOptions | ActionBar.DISPLAY_SHOW_CUSTOM); +        } + +        mContentHeight = a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0); + +        a.recycle(); + +        mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle); +        mHomeLayout.setOnClickListener(mUpClickListener); +        mHomeLayout.setClickable(true); +        mHomeLayout.setFocusable(true); +    } + +    /** +     * Attempt to programmatically load the logo from the manifest file of an +     * activity by using an XML pull parser. This should allow us to read the +     * logo attribute regardless of the platform it is being run on. +     * +     * @param activity Activity instance. +     * @return Logo resource ID. +     */ +    private static int loadLogoFromManifest(Activity activity) { +        int logo = 0; +        try { +            final String thisPackage = activity.getClass().getName(); +            if (DEBUG) Log.i(TAG, "Parsing AndroidManifest.xml for " + thisPackage); + +            final String packageName = activity.getApplicationInfo().packageName; +            final AssetManager am = activity.createPackageContext(packageName, 0).getAssets(); +            final XmlResourceParser xml = am.openXmlResourceParser("AndroidManifest.xml"); + +            int eventType = xml.getEventType(); +            while (eventType != XmlPullParser.END_DOCUMENT) { +                if (eventType == XmlPullParser.START_TAG) { +                    String name = xml.getName(); + +                    if ("application".equals(name)) { +                        //Check if the <application> has the attribute +                        if (DEBUG) Log.d(TAG, "Got <application>"); + +                        for (int i = xml.getAttributeCount() - 1; i >= 0; i--) { +                            if (DEBUG) Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i)); + +                            if ("logo".equals(xml.getAttributeName(i))) { +                                logo = xml.getAttributeResourceValue(i, 0); +                                break; //out of for loop +                            } +                        } +                    } else if ("activity".equals(name)) { +                        //Check if the <activity> is us and has the attribute +                        if (DEBUG) Log.d(TAG, "Got <activity>"); +                        Integer activityLogo = null; +                        String activityPackage = null; +                        boolean isOurActivity = false; + +                        for (int i = xml.getAttributeCount() - 1; i >= 0; i--) { +                            if (DEBUG) Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i)); + +                            //We need both uiOptions and name attributes +                            String attrName = xml.getAttributeName(i); +                            if ("logo".equals(attrName)) { +                                activityLogo = xml.getAttributeResourceValue(i, 0); +                            } else if ("name".equals(attrName)) { +                                activityPackage = ActionBarSherlockCompat.cleanActivityName(packageName, xml.getAttributeValue(i)); +                                if (!thisPackage.equals(activityPackage)) { +                                    break; //on to the next +                                } +                                isOurActivity = true; +                            } + +                            //Make sure we have both attributes before processing +                            if ((activityLogo != null) && (activityPackage != null)) { +                                //Our activity, logo specified, override with our value +                                logo = activityLogo.intValue(); +                            } +                        } +                        if (isOurActivity) { +                            //If we matched our activity but it had no logo don't +                            //do any more processing of the manifest +                            break; +                        } +                    } +                } +                eventType = xml.nextToken(); +            } +        } catch (Exception e) { +            e.printStackTrace(); +        } +        if (DEBUG) Log.i(TAG, "Returning " + Integer.toHexString(logo)); +        return logo; +    } + +    /* +     * Must be public so we can dispatch pre-2.2 via ActionBarImpl. +     */ +    @Override +    public void onConfigurationChanged(Configuration newConfig) { +        super.onConfigurationChanged(newConfig); + +        mTitleView = null; +        mSubtitleView = null; +        mTitleUpView = null; +        if (mTitleLayout != null && mTitleLayout.getParent() == this) { +            removeView(mTitleLayout); +        } +        mTitleLayout = null; +        if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) { +            initTitle(); +        } + +        if (mTabScrollView != null && mIncludeTabs) { +            ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams(); +            if (lp != null) { +                lp.width = LayoutParams.WRAP_CONTENT; +                lp.height = LayoutParams.MATCH_PARENT; +            } +            mTabScrollView.setAllowCollapse(true); +        } +    } + +    /** +     * Set the window callback used to invoke menu items; used for dispatching home button presses. +     * @param cb Window callback to dispatch to +     */ +    public void setWindowCallback(Window.Callback cb) { +        mWindowCallback = cb; +    } + +    @Override +    public void onDetachedFromWindow() { +        super.onDetachedFromWindow(); +        //UNUSED removeCallbacks(mTabSelector); +        if (mActionMenuPresenter != null) { +            mActionMenuPresenter.hideOverflowMenu(); +            mActionMenuPresenter.hideSubMenus(); +        } +    } + +    @Override +    public boolean shouldDelayChildPressedState() { +        return false; +    } + +    public void initProgress() { +        mProgressView = new IcsProgressBar(mContext, null, 0, mProgressStyle); +        mProgressView.setId(R.id.abs__progress_horizontal); +        mProgressView.setMax(10000); +        addView(mProgressView); +    } + +    public void initIndeterminateProgress() { +        mIndeterminateProgressView = new IcsProgressBar(mContext, null, 0, mIndeterminateProgressStyle); +        mIndeterminateProgressView.setId(R.id.abs__progress_circular); +        addView(mIndeterminateProgressView); +    } + +    @Override +    public void setSplitActionBar(boolean splitActionBar) { +        if (mSplitActionBar != splitActionBar) { +            if (mMenuView != null) { +                final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); +                if (oldParent != null) { +                    oldParent.removeView(mMenuView); +                } +                if (splitActionBar) { +                    if (mSplitView != null) { +                        mSplitView.addView(mMenuView); +                    } +                } else { +                    addView(mMenuView); +                } +            } +            if (mSplitView != null) { +                mSplitView.setVisibility(splitActionBar ? VISIBLE : GONE); +            } +            super.setSplitActionBar(splitActionBar); +        } +    } + +    public boolean isSplitActionBar() { +        return mSplitActionBar; +    } + +    public boolean hasEmbeddedTabs() { +        return mIncludeTabs; +    } + +    public void setEmbeddedTabView(ScrollingTabContainerView tabs) { +        if (mTabScrollView != null) { +            removeView(mTabScrollView); +        } +        mTabScrollView = tabs; +        mIncludeTabs = tabs != null; +        if (mIncludeTabs && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { +            addView(mTabScrollView); +            ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams(); +            lp.width = LayoutParams.WRAP_CONTENT; +            lp.height = LayoutParams.MATCH_PARENT; +            tabs.setAllowCollapse(true); +        } +    } + +    public void setCallback(OnNavigationListener callback) { +        mCallback = callback; +    } + +    public void setMenu(Menu menu, MenuPresenter.Callback cb) { +        if (menu == mOptionsMenu) return; + +        if (mOptionsMenu != null) { +            mOptionsMenu.removeMenuPresenter(mActionMenuPresenter); +            mOptionsMenu.removeMenuPresenter(mExpandedMenuPresenter); +        } + +        MenuBuilder builder = (MenuBuilder) menu; +        mOptionsMenu = builder; +        if (mMenuView != null) { +            final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); +            if (oldParent != null) { +                oldParent.removeView(mMenuView); +            } +        } +        if (mActionMenuPresenter == null) { +            mActionMenuPresenter = new ActionMenuPresenter(mContext); +            mActionMenuPresenter.setCallback(cb); +            mActionMenuPresenter.setId(R.id.abs__action_menu_presenter); +            mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter(); +        } + +        ActionMenuView menuView; +        final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, +                LayoutParams.MATCH_PARENT); +        if (!mSplitActionBar) { +            mActionMenuPresenter.setExpandedActionViewsExclusive( +                    getResources_getBoolean(getContext(), +                    R.bool.abs__action_bar_expanded_action_views_exclusive)); +            configPresenters(builder); +            menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); +            final ViewGroup oldParent = (ViewGroup) menuView.getParent(); +            if (oldParent != null && oldParent != this) { +                oldParent.removeView(menuView); +            } +            addView(menuView, layoutParams); +        } else { +            mActionMenuPresenter.setExpandedActionViewsExclusive(false); +            // Allow full screen width in split mode. +            mActionMenuPresenter.setWidthLimit( +                    getContext().getResources().getDisplayMetrics().widthPixels, true); +            // No limit to the item count; use whatever will fit. +            mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); +            // Span the whole width +            layoutParams.width = LayoutParams.MATCH_PARENT; +            configPresenters(builder); +            menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); +            if (mSplitView != null) { +                final ViewGroup oldParent = (ViewGroup) menuView.getParent(); +                if (oldParent != null && oldParent != mSplitView) { +                    oldParent.removeView(menuView); +                } +                menuView.setVisibility(getAnimatedVisibility()); +                mSplitView.addView(menuView, layoutParams); +            } else { +                // We'll add this later if we missed it this time. +                menuView.setLayoutParams(layoutParams); +            } +        } +        mMenuView = menuView; +    } + +    private void configPresenters(MenuBuilder builder) { +        if (builder != null) { +            builder.addMenuPresenter(mActionMenuPresenter); +            builder.addMenuPresenter(mExpandedMenuPresenter); +        } else { +            mActionMenuPresenter.initForMenu(mContext, null); +            mExpandedMenuPresenter.initForMenu(mContext, null); +            mActionMenuPresenter.updateMenuView(true); +            mExpandedMenuPresenter.updateMenuView(true); +        } +    } + +    public boolean hasExpandedActionView() { +        return mExpandedMenuPresenter != null && +                mExpandedMenuPresenter.mCurrentExpandedItem != null; +    } + +    public void collapseActionView() { +        final MenuItemImpl item = mExpandedMenuPresenter == null ? null : +                mExpandedMenuPresenter.mCurrentExpandedItem; +        if (item != null) { +            item.collapseActionView(); +        } +    } + +    public void setCustomNavigationView(View view) { +        final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0; +        if (mCustomNavView != null && showCustom) { +            removeView(mCustomNavView); +        } +        mCustomNavView = view; +        if (mCustomNavView != null && showCustom) { +            addView(mCustomNavView); +        } +    } + +    public CharSequence getTitle() { +        return mTitle; +    } + +    /** +     * Set the action bar title. This will always replace or override window titles. +     * @param title Title to set +     * +     * @see #setWindowTitle(CharSequence) +     */ +    public void setTitle(CharSequence title) { +        mUserTitle = true; +        setTitleImpl(title); +    } + +    /** +     * Set the window title. A window title will always be replaced or overridden by a user title. +     * @param title Title to set +     * +     * @see #setTitle(CharSequence) +     */ +    public void setWindowTitle(CharSequence title) { +        if (!mUserTitle) { +            setTitleImpl(title); +        } +    } + +    private void setTitleImpl(CharSequence title) { +        mTitle = title; +        if (mTitleView != null) { +            mTitleView.setText(title); +            final boolean visible = mExpandedActionView == null && +                    (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 && +                    (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle)); +            mTitleLayout.setVisibility(visible ? VISIBLE : GONE); +        } +        if (mLogoNavItem != null) { +            mLogoNavItem.setTitle(title); +        } +    } + +    public CharSequence getSubtitle() { +        return mSubtitle; +    } + +    public void setSubtitle(CharSequence subtitle) { +        mSubtitle = subtitle; +        if (mSubtitleView != null) { +            mSubtitleView.setText(subtitle); +            mSubtitleView.setVisibility(subtitle != null ? VISIBLE : GONE); +            final boolean visible = mExpandedActionView == null && +                    (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 && +                    (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle)); +            mTitleLayout.setVisibility(visible ? VISIBLE : GONE); +        } +    } + +    public void setHomeButtonEnabled(boolean enable) { +        mHomeLayout.setEnabled(enable); +        mHomeLayout.setFocusable(enable); +        // Make sure the home button has an accurate content description for accessibility. +        if (!enable) { +            mHomeLayout.setContentDescription(null); +        } else if ((mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0) { +            mHomeLayout.setContentDescription(mContext.getResources().getText( +                    R.string.abs__action_bar_up_description)); +        } else { +            mHomeLayout.setContentDescription(mContext.getResources().getText( +                    R.string.abs__action_bar_home_description)); +        } +    } + +    public void setDisplayOptions(int options) { +        final int flagsChanged = mDisplayOptions == -1 ? -1 : options ^ mDisplayOptions; +        mDisplayOptions = options; + +        if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) { +            final boolean showHome = (options & ActionBar.DISPLAY_SHOW_HOME) != 0; +            final int vis = showHome && mExpandedActionView == null ? VISIBLE : GONE; +            mHomeLayout.setVisibility(vis); + +            if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) { +                final boolean setUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0; +                mHomeLayout.setUp(setUp); + +                // Showing home as up implicitly enables interaction with it. +                // In honeycomb it was always enabled, so make this transition +                // a bit easier for developers in the common case. +                // (It would be silly to show it as up without responding to it.) +                if (setUp) { +                    setHomeButtonEnabled(true); +                } +            } + +            if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) { +                final boolean logoVis = mLogo != null && (options & ActionBar.DISPLAY_USE_LOGO) != 0; +                mHomeLayout.setIcon(logoVis ? mLogo : mIcon); +            } + +            if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) { +                if ((options & ActionBar.DISPLAY_SHOW_TITLE) != 0) { +                    initTitle(); +                } else { +                    removeView(mTitleLayout); +                } +            } + +            if (mTitleLayout != null && (flagsChanged & +                    (ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME)) != 0) { +                final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0; +                mTitleUpView.setVisibility(!showHome ? (homeAsUp ? VISIBLE : INVISIBLE) : GONE); +                mTitleLayout.setEnabled(!showHome && homeAsUp); +            } + +            if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) { +                if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { +                    addView(mCustomNavView); +                } else { +                    removeView(mCustomNavView); +                } +            } + +            requestLayout(); +        } else { +            invalidate(); +        } + +        // Make sure the home button has an accurate content description for accessibility. +        if (!mHomeLayout.isEnabled()) { +            mHomeLayout.setContentDescription(null); +        } else if ((options & ActionBar.DISPLAY_HOME_AS_UP) != 0) { +            mHomeLayout.setContentDescription(mContext.getResources().getText( +                    R.string.abs__action_bar_up_description)); +        } else { +            mHomeLayout.setContentDescription(mContext.getResources().getText( +                    R.string.abs__action_bar_home_description)); +        } +    } + +    public void setIcon(Drawable icon) { +        mIcon = icon; +        if (icon != null && +                ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null)) { +            mHomeLayout.setIcon(icon); +        } +    } + +    public void setIcon(int resId) { +        setIcon(mContext.getResources().getDrawable(resId)); +    } + +    public void setLogo(Drawable logo) { +        mLogo = logo; +        if (logo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) { +            mHomeLayout.setIcon(logo); +        } +    } + +    public void setLogo(int resId) { +        setLogo(mContext.getResources().getDrawable(resId)); +    } + +    public void setNavigationMode(int mode) { +        final int oldMode = mNavigationMode; +        if (mode != oldMode) { +            switch (oldMode) { +            case ActionBar.NAVIGATION_MODE_LIST: +                if (mListNavLayout != null) { +                    removeView(mListNavLayout); +                } +                break; +            case ActionBar.NAVIGATION_MODE_TABS: +                if (mTabScrollView != null && mIncludeTabs) { +                    removeView(mTabScrollView); +                } +            } + +            switch (mode) { +            case ActionBar.NAVIGATION_MODE_LIST: +                if (mSpinner == null) { +                    mSpinner = new IcsSpinner(mContext, null, +                            R.attr.actionDropDownStyle); +                    mListNavLayout = (IcsLinearLayout) LayoutInflater.from(mContext) +                            .inflate(R.layout.abs__action_bar_tab_bar_view, null); +                    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( +                            LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); +                    params.gravity = Gravity.CENTER; +                    mListNavLayout.addView(mSpinner, params); +                } +                if (mSpinner.getAdapter() != mSpinnerAdapter) { +                    mSpinner.setAdapter(mSpinnerAdapter); +                } +                mSpinner.setOnItemSelectedListener(mNavItemSelectedListener); +                addView(mListNavLayout); +                break; +            case ActionBar.NAVIGATION_MODE_TABS: +                if (mTabScrollView != null && mIncludeTabs) { +                    addView(mTabScrollView); +                } +                break; +            } +            mNavigationMode = mode; +            requestLayout(); +        } +    } + +    public void setDropdownAdapter(SpinnerAdapter adapter) { +        mSpinnerAdapter = adapter; +        if (mSpinner != null) { +            mSpinner.setAdapter(adapter); +        } +    } + +    public SpinnerAdapter getDropdownAdapter() { +        return mSpinnerAdapter; +    } + +    public void setDropdownSelectedPosition(int position) { +        mSpinner.setSelection(position); +    } + +    public int getDropdownSelectedPosition() { +        return mSpinner.getSelectedItemPosition(); +    } + +    public View getCustomNavigationView() { +        return mCustomNavView; +    } + +    public int getNavigationMode() { +        return mNavigationMode; +    } + +    public int getDisplayOptions() { +        return mDisplayOptions; +    } + +    @Override +    protected ViewGroup.LayoutParams generateDefaultLayoutParams() { +        // Used by custom nav views if they don't supply layout params. Everything else +        // added to an ActionBarView should have them already. +        return new ActionBar.LayoutParams(DEFAULT_CUSTOM_GRAVITY); +    } + +    @Override +    protected void onFinishInflate() { +        super.onFinishInflate(); + +        addView(mHomeLayout); + +        if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { +            final ViewParent parent = mCustomNavView.getParent(); +            if (parent != this) { +                if (parent instanceof ViewGroup) { +                    ((ViewGroup) parent).removeView(mCustomNavView); +                } +                addView(mCustomNavView); +            } +        } +    } + +    private void initTitle() { +        if (mTitleLayout == null) { +            LayoutInflater inflater = LayoutInflater.from(getContext()); +            mTitleLayout = (LinearLayout) inflater.inflate(R.layout.abs__action_bar_title_item, +                    this, false); +            mTitleView = (TextView) mTitleLayout.findViewById(R.id.abs__action_bar_title); +            mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.abs__action_bar_subtitle); +            mTitleUpView = mTitleLayout.findViewById(R.id.abs__up); + +            mTitleLayout.setOnClickListener(mUpClickListener); + +            if (mTitleStyleRes != 0) { +                mTitleView.setTextAppearance(mContext, mTitleStyleRes); +            } +            if (mTitle != null) { +                mTitleView.setText(mTitle); +            } + +            if (mSubtitleStyleRes != 0) { +                mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes); +            } +            if (mSubtitle != null) { +                mSubtitleView.setText(mSubtitle); +                mSubtitleView.setVisibility(VISIBLE); +            } + +            final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0; +            final boolean showHome = (mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0; +            mTitleUpView.setVisibility(!showHome ? (homeAsUp ? VISIBLE : INVISIBLE) : GONE); +            mTitleLayout.setEnabled(homeAsUp && !showHome); +        } + +        addView(mTitleLayout); +        if (mExpandedActionView != null || +                (TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle))) { +            // Don't show while in expanded mode or with empty text +            mTitleLayout.setVisibility(GONE); +        } +    } + +    public void setContextView(ActionBarContextView view) { +        mContextView = view; +    } + +    public void setCollapsable(boolean collapsable) { +        mIsCollapsable = collapsable; +    } + +    public boolean isCollapsed() { +        return mIsCollapsed; +    } + +    @Override +    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        final int childCount = getChildCount(); +        if (mIsCollapsable) { +            int visibleChildren = 0; +            for (int i = 0; i < childCount; i++) { +                final View child = getChildAt(i); +                if (child.getVisibility() != GONE && +                        !(child == mMenuView && mMenuView.getChildCount() == 0)) { +                    visibleChildren++; +                } +            } + +            if (visibleChildren == 0) { +                // No size for an empty action bar when collapsable. +                setMeasuredDimension(0, 0); +                mIsCollapsed = true; +                return; +            } +        } +        mIsCollapsed = false; + +        int widthMode = MeasureSpec.getMode(widthMeasureSpec); +        if (widthMode != MeasureSpec.EXACTLY) { +            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + +                    "with android:layout_width=\"match_parent\" (or fill_parent)"); +        } + +        int heightMode = MeasureSpec.getMode(heightMeasureSpec); +        if (heightMode != MeasureSpec.AT_MOST) { +            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + +                    "with android:layout_height=\"wrap_content\""); +        } + +        int contentWidth = MeasureSpec.getSize(widthMeasureSpec); + +        int maxHeight = mContentHeight > 0 ? +                mContentHeight : MeasureSpec.getSize(heightMeasureSpec); + +        final int verticalPadding = getPaddingTop() + getPaddingBottom(); +        final int paddingLeft = getPaddingLeft(); +        final int paddingRight = getPaddingRight(); +        final int height = maxHeight - verticalPadding; +        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); + +        int availableWidth = contentWidth - paddingLeft - paddingRight; +        int leftOfCenter = availableWidth / 2; +        int rightOfCenter = leftOfCenter; + +        HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout; + +        if (homeLayout.getVisibility() != GONE) { +            final ViewGroup.LayoutParams lp = homeLayout.getLayoutParams(); +            int homeWidthSpec; +            if (lp.width < 0) { +                homeWidthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST); +            } else { +                homeWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY); +            } +            homeLayout.measure(homeWidthSpec, +                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); +            final int homeWidth = homeLayout.getMeasuredWidth() + homeLayout.getLeftOffset(); +            availableWidth = Math.max(0, availableWidth - homeWidth); +            leftOfCenter = Math.max(0, availableWidth - homeWidth); +        } + +        if (mMenuView != null && mMenuView.getParent() == this) { +            availableWidth = measureChildView(mMenuView, availableWidth, +                    childSpecHeight, 0); +            rightOfCenter = Math.max(0, rightOfCenter - mMenuView.getMeasuredWidth()); +        } + +        if (mIndeterminateProgressView != null && +                mIndeterminateProgressView.getVisibility() != GONE) { +            availableWidth = measureChildView(mIndeterminateProgressView, availableWidth, +                    childSpecHeight, 0); +            rightOfCenter = Math.max(0, +                    rightOfCenter - mIndeterminateProgressView.getMeasuredWidth()); +        } + +        final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE && +                (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0; + +        if (mExpandedActionView == null) { +            switch (mNavigationMode) { +                case ActionBar.NAVIGATION_MODE_LIST: +                    if (mListNavLayout != null) { +                        final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding; +                        availableWidth = Math.max(0, availableWidth - itemPaddingSize); +                        leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize); +                        mListNavLayout.measure( +                                MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), +                                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); +                        final int listNavWidth = mListNavLayout.getMeasuredWidth(); +                        availableWidth = Math.max(0, availableWidth - listNavWidth); +                        leftOfCenter = Math.max(0, leftOfCenter - listNavWidth); +                    } +                    break; +                case ActionBar.NAVIGATION_MODE_TABS: +                    if (mTabScrollView != null) { +                        final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding; +                        availableWidth = Math.max(0, availableWidth - itemPaddingSize); +                        leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize); +                        mTabScrollView.measure( +                                MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), +                                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); +                        final int tabWidth = mTabScrollView.getMeasuredWidth(); +                        availableWidth = Math.max(0, availableWidth - tabWidth); +                        leftOfCenter = Math.max(0, leftOfCenter - tabWidth); +                    } +                    break; +            } +        } + +        View customView = null; +        if (mExpandedActionView != null) { +            customView = mExpandedActionView; +        } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && +                mCustomNavView != null) { +            customView = mCustomNavView; +        } + +        if (customView != null) { +            final ViewGroup.LayoutParams lp = generateLayoutParams(customView.getLayoutParams()); +            final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? +                    (ActionBar.LayoutParams) lp : null; + +            int horizontalMargin = 0; +            int verticalMargin = 0; +            if (ablp != null) { +                horizontalMargin = ablp.leftMargin + ablp.rightMargin; +                verticalMargin = ablp.topMargin + ablp.bottomMargin; +            } + +            // If the action bar is wrapping to its content height, don't allow a custom +            // view to MATCH_PARENT. +            int customNavHeightMode; +            if (mContentHeight <= 0) { +                customNavHeightMode = MeasureSpec.AT_MOST; +            } else { +                customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ? +                        MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; +            } +            final int customNavHeight = Math.max(0, +                    (lp.height >= 0 ? Math.min(lp.height, height) : height) - verticalMargin); + +            final int customNavWidthMode = lp.width != LayoutParams.WRAP_CONTENT ? +                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; +            int customNavWidth = Math.max(0, +                    (lp.width >= 0 ? Math.min(lp.width, availableWidth) : availableWidth) +                    - horizontalMargin); +            final int hgrav = (ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY) & +                    Gravity.HORIZONTAL_GRAVITY_MASK; + +            // Centering a custom view is treated specially; we try to center within the whole +            // action bar rather than in the available space. +            if (hgrav == Gravity.CENTER_HORIZONTAL && lp.width == LayoutParams.MATCH_PARENT) { +                customNavWidth = Math.min(leftOfCenter, rightOfCenter) * 2; +            } + +            customView.measure( +                    MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode), +                    MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode)); +            availableWidth -= horizontalMargin + customView.getMeasuredWidth(); +        } + +        if (mExpandedActionView == null && showTitle) { +            availableWidth = measureChildView(mTitleLayout, availableWidth, +                    MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY), 0); +            leftOfCenter = Math.max(0, leftOfCenter - mTitleLayout.getMeasuredWidth()); +        } + +        if (mContentHeight <= 0) { +            int measuredHeight = 0; +            for (int i = 0; i < childCount; i++) { +                View v = getChildAt(i); +                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding; +                if (paddedViewHeight > measuredHeight) { +                    measuredHeight = paddedViewHeight; +                } +            } +            setMeasuredDimension(contentWidth, measuredHeight); +        } else { +            setMeasuredDimension(contentWidth, maxHeight); +        } + +        if (mContextView != null) { +            mContextView.setContentHeight(getMeasuredHeight()); +        } + +        if (mProgressView != null && mProgressView.getVisibility() != GONE) { +            mProgressView.measure(MeasureSpec.makeMeasureSpec( +                    contentWidth - mProgressBarPadding * 2, MeasureSpec.EXACTLY), +                    MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST)); +        } +    } + +    @Override +    protected void onLayout(boolean changed, int l, int t, int r, int b) { +        int x = getPaddingLeft(); +        final int y = getPaddingTop(); +        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom(); + +        if (contentHeight <= 0) { +            // Nothing to do if we can't see anything. +            return; +        } + +        HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout; +        if (homeLayout.getVisibility() != GONE) { +            final int leftOffset = homeLayout.getLeftOffset(); +            x += positionChild(homeLayout, x + leftOffset, y, contentHeight) + leftOffset; +        } + +        if (mExpandedActionView == null) { +            final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE && +                    (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0; +            if (showTitle) { +                x += positionChild(mTitleLayout, x, y, contentHeight); +            } + +            switch (mNavigationMode) { +                case ActionBar.NAVIGATION_MODE_STANDARD: +                    break; +                case ActionBar.NAVIGATION_MODE_LIST: +                    if (mListNavLayout != null) { +                        if (showTitle) x += mItemPadding; +                        x += positionChild(mListNavLayout, x, y, contentHeight) + mItemPadding; +                    } +                    break; +                case ActionBar.NAVIGATION_MODE_TABS: +                    if (mTabScrollView != null) { +                        if (showTitle) x += mItemPadding; +                        x += positionChild(mTabScrollView, x, y, contentHeight) + mItemPadding; +                    } +                    break; +            } +        } + +        int menuLeft = r - l - getPaddingRight(); +        if (mMenuView != null && mMenuView.getParent() == this) { +            positionChildInverse(mMenuView, menuLeft, y, contentHeight); +            menuLeft -= mMenuView.getMeasuredWidth(); +        } + +        if (mIndeterminateProgressView != null && +                mIndeterminateProgressView.getVisibility() != GONE) { +            positionChildInverse(mIndeterminateProgressView, menuLeft, y, contentHeight); +            menuLeft -= mIndeterminateProgressView.getMeasuredWidth(); +        } + +        View customView = null; +        if (mExpandedActionView != null) { +            customView = mExpandedActionView; +        } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && +                mCustomNavView != null) { +            customView = mCustomNavView; +        } +        if (customView != null) { +            ViewGroup.LayoutParams lp = customView.getLayoutParams(); +            final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? +                    (ActionBar.LayoutParams) lp : null; + +            final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY; +            final int navWidth = customView.getMeasuredWidth(); + +            int topMargin = 0; +            int bottomMargin = 0; +            if (ablp != null) { +                x += ablp.leftMargin; +                menuLeft -= ablp.rightMargin; +                topMargin = ablp.topMargin; +                bottomMargin = ablp.bottomMargin; +            } + +            int hgravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK; +            // See if we actually have room to truly center; if not push against left or right. +            if (hgravity == Gravity.CENTER_HORIZONTAL) { +                final int centeredLeft = ((getRight() - getLeft()) - navWidth) / 2; +                if (centeredLeft < x) { +                    hgravity = Gravity.LEFT; +                } else if (centeredLeft + navWidth > menuLeft) { +                    hgravity = Gravity.RIGHT; +                } +            } else if (gravity == -1) { +                hgravity = Gravity.LEFT; +            } + +            int xpos = 0; +            switch (hgravity) { +                case Gravity.CENTER_HORIZONTAL: +                    xpos = ((getRight() - getLeft()) - navWidth) / 2; +                    break; +                case Gravity.LEFT: +                    xpos = x; +                    break; +                case Gravity.RIGHT: +                    xpos = menuLeft - navWidth; +                    break; +            } + +            int vgravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; + +            if (gravity == -1) { +                vgravity = Gravity.CENTER_VERTICAL; +            } + +            int ypos = 0; +            switch (vgravity) { +                case Gravity.CENTER_VERTICAL: +                    final int paddedTop = getPaddingTop(); +                    final int paddedBottom = getBottom() - getTop() - getPaddingBottom(); +                    ypos = ((paddedBottom - paddedTop) - customView.getMeasuredHeight()) / 2; +                    break; +                case Gravity.TOP: +                    ypos = getPaddingTop() + topMargin; +                    break; +                case Gravity.BOTTOM: +                    ypos = getHeight() - getPaddingBottom() - customView.getMeasuredHeight() +                            - bottomMargin; +                    break; +            } +            final int customWidth = customView.getMeasuredWidth(); +            customView.layout(xpos, ypos, xpos + customWidth, +                    ypos + customView.getMeasuredHeight()); +            x += customWidth; +        } + +        if (mProgressView != null) { +            mProgressView.bringToFront(); +            final int halfProgressHeight = mProgressView.getMeasuredHeight() / 2; +            mProgressView.layout(mProgressBarPadding, -halfProgressHeight, +                    mProgressBarPadding + mProgressView.getMeasuredWidth(), halfProgressHeight); +        } +    } + +    @Override +    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { +        return new ActionBar.LayoutParams(getContext(), attrs); +    } + +    @Override +    public ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { +        if (lp == null) { +            lp = generateDefaultLayoutParams(); +        } +        return lp; +    } + +    @Override +    public Parcelable onSaveInstanceState() { +        Parcelable superState = super.onSaveInstanceState(); +        SavedState state = new SavedState(superState); + +        if (mExpandedMenuPresenter != null && mExpandedMenuPresenter.mCurrentExpandedItem != null) { +            state.expandedMenuItemId = mExpandedMenuPresenter.mCurrentExpandedItem.getItemId(); +        } + +        state.isOverflowOpen = isOverflowMenuShowing(); + +        return state; +    } + +    @Override +    public void onRestoreInstanceState(Parcelable p) { +        SavedState state = (SavedState) p; + +        super.onRestoreInstanceState(state.getSuperState()); + +        if (state.expandedMenuItemId != 0 && +                mExpandedMenuPresenter != null && mOptionsMenu != null) { +            final MenuItem item = mOptionsMenu.findItem(state.expandedMenuItemId); +            if (item != null) { +                item.expandActionView(); +            } +        } + +        if (state.isOverflowOpen) { +            postShowOverflowMenu(); +        } +    } + +    static class SavedState extends BaseSavedState { +        int expandedMenuItemId; +        boolean isOverflowOpen; + +        SavedState(Parcelable superState) { +            super(superState); +        } + +        private SavedState(Parcel in) { +            super(in); +            expandedMenuItemId = in.readInt(); +            isOverflowOpen = in.readInt() != 0; +        } + +        @Override +        public void writeToParcel(Parcel out, int flags) { +            super.writeToParcel(out, flags); +            out.writeInt(expandedMenuItemId); +            out.writeInt(isOverflowOpen ? 1 : 0); +        } + +        public static final Parcelable.Creator<SavedState> CREATOR = +                new Parcelable.Creator<SavedState>() { +            public SavedState createFromParcel(Parcel in) { +                return new SavedState(in); +            } + +            public SavedState[] newArray(int size) { +                return new SavedState[size]; +            } +        }; +    } + +    public static class HomeView extends FrameLayout { +        private View mUpView; +        private ImageView mIconView; +        private int mUpWidth; + +        public HomeView(Context context) { +            this(context, null); +        } + +        public HomeView(Context context, AttributeSet attrs) { +            super(context, attrs); +        } + +        public void setUp(boolean isUp) { +            mUpView.setVisibility(isUp ? VISIBLE : GONE); +        } + +        public void setIcon(Drawable icon) { +            mIconView.setImageDrawable(icon); +        } + +        @Override +        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { +            onPopulateAccessibilityEvent(event); +            return true; +        } + +        @Override +        public void onPopulateAccessibilityEvent(AccessibilityEvent event) { +            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { +                super.onPopulateAccessibilityEvent(event); +            } +            final CharSequence cdesc = getContentDescription(); +            if (!TextUtils.isEmpty(cdesc)) { +                event.getText().add(cdesc); +            } +        } + +        @Override +        public boolean dispatchHoverEvent(MotionEvent event) { +            // Don't allow children to hover; we want this to be treated as a single component. +            return onHoverEvent(event); +        } + +        @Override +        protected void onFinishInflate() { +            mUpView = findViewById(R.id.abs__up); +            mIconView = (ImageView) findViewById(R.id.abs__home); +        } + +        public int getLeftOffset() { +            return mUpView.getVisibility() == GONE ? mUpWidth : 0; +        } + +        @Override +        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +            measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0); +            final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); +            mUpWidth = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin; +            int width = mUpView.getVisibility() == GONE ? 0 : mUpWidth; +            int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin; +            measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0); +            final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); +            width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin; +            height = Math.max(height, +                    iconLp.topMargin + mIconView.getMeasuredHeight() + iconLp.bottomMargin); + +            final int widthMode = MeasureSpec.getMode(widthMeasureSpec); +            final int heightMode = MeasureSpec.getMode(heightMeasureSpec); +            final int widthSize = MeasureSpec.getSize(widthMeasureSpec); +            final int heightSize = MeasureSpec.getSize(heightMeasureSpec); + +            switch (widthMode) { +                case MeasureSpec.AT_MOST: +                    width = Math.min(width, widthSize); +                    break; +                case MeasureSpec.EXACTLY: +                    width = widthSize; +                    break; +                case MeasureSpec.UNSPECIFIED: +                default: +                    break; +            } +            switch (heightMode) { +                case MeasureSpec.AT_MOST: +                    height = Math.min(height, heightSize); +                    break; +                case MeasureSpec.EXACTLY: +                    height = heightSize; +                    break; +                case MeasureSpec.UNSPECIFIED: +                default: +                    break; +            } +            setMeasuredDimension(width, height); +        } + +        @Override +        protected void onLayout(boolean changed, int l, int t, int r, int b) { +            final int vCenter = (b - t) / 2; +            //UNUSED int width = r - l; +            int upOffset = 0; +            if (mUpView.getVisibility() != GONE) { +                final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); +                final int upHeight = mUpView.getMeasuredHeight(); +                final int upWidth = mUpView.getMeasuredWidth(); +                final int upTop = vCenter - upHeight / 2; +                mUpView.layout(0, upTop, upWidth, upTop + upHeight); +                upOffset = upLp.leftMargin + upWidth + upLp.rightMargin; +                //UNUSED width -= upOffset; +                l += upOffset; +            } +            final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); +            final int iconHeight = mIconView.getMeasuredHeight(); +            final int iconWidth = mIconView.getMeasuredWidth(); +            final int hCenter = (r - l) / 2; +            final int iconLeft = upOffset + Math.max(iconLp.leftMargin, hCenter - iconWidth / 2); +            final int iconTop = Math.max(iconLp.topMargin, vCenter - iconHeight / 2); +            mIconView.layout(iconLeft, iconTop, iconLeft + iconWidth, iconTop + iconHeight); +        } +    } + +    private class ExpandedActionViewMenuPresenter implements MenuPresenter { +        MenuBuilder mMenu; +        MenuItemImpl mCurrentExpandedItem; + +        @Override +        public void initForMenu(Context context, MenuBuilder menu) { +            // Clear the expanded action view when menus change. +            if (mMenu != null && mCurrentExpandedItem != null) { +                mMenu.collapseItemActionView(mCurrentExpandedItem); +            } +            mMenu = menu; +        } + +        @Override +        public MenuView getMenuView(ViewGroup root) { +            return null; +        } + +        @Override +        public void updateMenuView(boolean cleared) { +            // Make sure the expanded item we have is still there. +            if (mCurrentExpandedItem != null) { +                boolean found = false; + +                if (mMenu != null) { +                    final int count = mMenu.size(); +                    for (int i = 0; i < count; i++) { +                        final MenuItem item = mMenu.getItem(i); +                        if (item == mCurrentExpandedItem) { +                            found = true; +                            break; +                        } +                    } +                } + +                if (!found) { +                    // The item we had expanded disappeared. Collapse. +                    collapseItemActionView(mMenu, mCurrentExpandedItem); +                } +            } +        } + +        @Override +        public void setCallback(Callback cb) { +        } + +        @Override +        public boolean onSubMenuSelected(SubMenuBuilder subMenu) { +            return false; +        } + +        @Override +        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { +        } + +        @Override +        public boolean flagActionItems() { +            return false; +        } + +        @Override +        public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { +            mExpandedActionView = item.getActionView(); +            mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(/* TODO getResources() */)); +            mCurrentExpandedItem = item; +            if (mExpandedActionView.getParent() != ActionBarView.this) { +                addView(mExpandedActionView); +            } +            if (mExpandedHomeLayout.getParent() != ActionBarView.this) { +                addView(mExpandedHomeLayout); +            } +            mHomeLayout.setVisibility(GONE); +            if (mTitleLayout != null) mTitleLayout.setVisibility(GONE); +            if (mTabScrollView != null) mTabScrollView.setVisibility(GONE); +            if (mSpinner != null) mSpinner.setVisibility(GONE); +            if (mCustomNavView != null) mCustomNavView.setVisibility(GONE); +            requestLayout(); +            item.setActionViewExpanded(true); + +            if (mExpandedActionView instanceof CollapsibleActionView) { +                ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded(); +            } + +            return true; +        } + +        @Override +        public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { +            // Do this before detaching the actionview from the hierarchy, in case +            // it needs to dismiss the soft keyboard, etc. +            if (mExpandedActionView instanceof CollapsibleActionView) { +                ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed(); +            } + +            removeView(mExpandedActionView); +            removeView(mExpandedHomeLayout); +            mExpandedActionView = null; +            if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) { +                mHomeLayout.setVisibility(VISIBLE); +            } +            if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) { +                if (mTitleLayout == null) { +                    initTitle(); +                } else { +                    mTitleLayout.setVisibility(VISIBLE); +                } +            } +            if (mTabScrollView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { +                mTabScrollView.setVisibility(VISIBLE); +            } +            if (mSpinner != null && mNavigationMode == ActionBar.NAVIGATION_MODE_LIST) { +                mSpinner.setVisibility(VISIBLE); +            } +            if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { +                mCustomNavView.setVisibility(VISIBLE); +            } +            mExpandedHomeLayout.setIcon(null); +            mCurrentExpandedItem = null; +            requestLayout(); +            item.setActionViewExpanded(false); + +            return true; +        } + +        @Override +        public int getId() { +            return 0; +        } + +        @Override +        public Parcelable onSaveInstanceState() { +            return null; +        } + +        @Override +        public void onRestoreInstanceState(Parcelable state) { +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingButton.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingButton.java new file mode 100644 index 000000000..fa3698f3b --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingButton.java @@ -0,0 +1,40 @@ +package com.actionbarsherlock.internal.widget; + +import java.util.Locale; +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Build; +import android.util.AttributeSet; +import android.widget.Button; + +public class CapitalizingButton extends Button { +    private static final boolean SANS_ICE_CREAM = Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH; +    private static final boolean IS_GINGERBREAD = Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; + +    private static final int[] R_styleable_Button = new int[] { +        android.R.attr.textAllCaps +    }; +    private static final int R_styleable_Button_textAllCaps = 0; + +    private boolean mAllCaps; + +    public CapitalizingButton(Context context, AttributeSet attrs) { +        super(context, attrs); + +        TypedArray a = context.obtainStyledAttributes(attrs, R_styleable_Button); +        mAllCaps = a.getBoolean(R_styleable_Button_textAllCaps, true); +        a.recycle(); +    } + +    public void setTextCompat(CharSequence text) { +        if (SANS_ICE_CREAM && mAllCaps && text != null) { +            if (IS_GINGERBREAD) { +                setText(text.toString().toUpperCase(Locale.ROOT)); +            } else { +                setText(text.toString().toUpperCase()); +            } +        } else { +            setText(text); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingTextView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingTextView.java new file mode 100644 index 000000000..cae8b8aed --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingTextView.java @@ -0,0 +1,50 @@ +package com.actionbarsherlock.internal.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Build; +import android.util.AttributeSet; +import android.widget.TextView; + +import java.util.Locale; + +public class CapitalizingTextView extends TextView { +    private static final boolean SANS_ICE_CREAM = Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH; +    private static final boolean IS_GINGERBREAD = Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; + +    private static final int[] R_styleable_TextView = new int[] { +        android.R.attr.textAllCaps +    }; +    private static final int R_styleable_TextView_textAllCaps = 0; + +    private boolean mAllCaps; + +    public CapitalizingTextView(Context context, AttributeSet attrs) { +        this(context, attrs, 0); +    } + +    public CapitalizingTextView(Context context, AttributeSet attrs, int defStyle) { +        super(context, attrs, defStyle); + +        TypedArray a = context.obtainStyledAttributes(attrs, R_styleable_TextView, defStyle, 0); +        mAllCaps = a.getBoolean(R_styleable_TextView_textAllCaps, true); +        a.recycle(); +    } + +    public void setTextCompat(CharSequence text) { +        if (SANS_ICE_CREAM && mAllCaps && text != null) { +            if (IS_GINGERBREAD) { +                try { +                    setText(text.toString().toUpperCase(Locale.ROOT)); +                } catch (NoSuchFieldError e) { +                    //Some manufacturer broke Locale.ROOT. See #572. +                    setText(text.toString().toUpperCase()); +                } +            } else { +                setText(text.toString().toUpperCase()); +            } +        } else { +            setText(text); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CollapsibleActionViewWrapper.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CollapsibleActionViewWrapper.java new file mode 100644 index 000000000..14f092c81 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CollapsibleActionViewWrapper.java @@ -0,0 +1,30 @@ +package com.actionbarsherlock.internal.widget; + +import android.view.View; +import android.widget.FrameLayout; +import com.actionbarsherlock.view.CollapsibleActionView; + +/** + * Wraps an ABS collapsible action view in a native container that delegates the calls. + */ +public class CollapsibleActionViewWrapper extends FrameLayout implements android.view.CollapsibleActionView { +    private final CollapsibleActionView child; + +    public CollapsibleActionViewWrapper(View child) { +        super(child.getContext()); +        this.child = (CollapsibleActionView) child; +        addView(child); +    } + +    @Override public void onActionViewExpanded() { +        child.onActionViewExpanded(); +    } + +    @Override public void onActionViewCollapsed() { +        child.onActionViewCollapsed(); +    } + +    public View unwrap() { +        return getChildAt(0); +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/FakeDialogPhoneWindow.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/FakeDialogPhoneWindow.java new file mode 100644 index 000000000..ad1b4f0a8 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/FakeDialogPhoneWindow.java @@ -0,0 +1,64 @@ +package com.actionbarsherlock.internal.widget; + +import static android.view.View.MeasureSpec.EXACTLY; +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.widget.LinearLayout; +import com.actionbarsherlock.R; + +public class FakeDialogPhoneWindow extends LinearLayout { +    final TypedValue mMinWidthMajor = new TypedValue(); +    final TypedValue mMinWidthMinor = new TypedValue(); + +    public FakeDialogPhoneWindow(Context context, AttributeSet attrs) { +        super(context, attrs); + +        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SherlockTheme); + +        a.getValue(R.styleable.SherlockTheme_windowMinWidthMajor, mMinWidthMajor); +        a.getValue(R.styleable.SherlockTheme_windowMinWidthMinor, mMinWidthMinor); + +        a.recycle(); +    } + +    /* Stolen from PhoneWindow */ +    @Override +    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); +        final boolean isPortrait = metrics.widthPixels < metrics.heightPixels; + +        super.onMeasure(widthMeasureSpec, heightMeasureSpec); + +        int width = getMeasuredWidth(); +        boolean measure = false; + +        widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY); + +        final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor; + +        if (tv.type != TypedValue.TYPE_NULL) { +            final int min; +            if (tv.type == TypedValue.TYPE_DIMENSION) { +                min = (int)tv.getDimension(metrics); +            } else if (tv.type == TypedValue.TYPE_FRACTION) { +                min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels); +            } else { +                min = 0; +            } + +            if (width < min) { +                widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY); +                measure = true; +            } +        } + +        // TODO: Support height? + +        if (measure) { +            super.onMeasure(widthMeasureSpec, heightMeasureSpec); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAbsSpinner.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAbsSpinner.java new file mode 100644 index 000000000..ce0cb3bca --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAbsSpinner.java @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2006 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.actionbarsherlock.internal.widget; + +import android.content.Context; +import android.database.DataSetObserver; +import android.graphics.Rect; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.view.View; +import android.view.ViewGroup; +import android.widget.SpinnerAdapter; + +/** + * An abstract base class for spinner widgets. SDK users will probably not + * need to use this class. + * + * @attr ref android.R.styleable#AbsSpinner_entries + */ +public abstract class IcsAbsSpinner extends IcsAdapterView<SpinnerAdapter> { +    private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; + +    SpinnerAdapter mAdapter; + +    int mHeightMeasureSpec; +    int mWidthMeasureSpec; +    boolean mBlockLayoutRequests; + +    int mSelectionLeftPadding = 0; +    int mSelectionTopPadding = 0; +    int mSelectionRightPadding = 0; +    int mSelectionBottomPadding = 0; +    final Rect mSpinnerPadding = new Rect(); + +    final RecycleBin mRecycler = new RecycleBin(); +    private DataSetObserver mDataSetObserver; + +    /** Temporary frame to hold a child View's frame rectangle */ +    private Rect mTouchFrame; + +    public IcsAbsSpinner(Context context) { +        super(context); +        initAbsSpinner(); +    } + +    public IcsAbsSpinner(Context context, AttributeSet attrs) { +        this(context, attrs, 0); +    } + +    public IcsAbsSpinner(Context context, AttributeSet attrs, int defStyle) { +        super(context, attrs, defStyle); +        initAbsSpinner(); + +        /* +        TypedArray a = context.obtainStyledAttributes(attrs, +                com.android.internal.R.styleable.AbsSpinner, defStyle, 0); + +        CharSequence[] entries = a.getTextArray(R.styleable.AbsSpinner_entries); +        if (entries != null) { +            ArrayAdapter<CharSequence> adapter = +                    new ArrayAdapter<CharSequence>(context, +                            R.layout.simple_spinner_item, entries); +            adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item); +            setAdapter(adapter); +        } + +        a.recycle(); +        */ +    } + +    /** +     * Common code for different constructor flavors +     */ +    private void initAbsSpinner() { +        setFocusable(true); +        setWillNotDraw(false); +    } + +    /** +     * The Adapter is used to provide the data which backs this Spinner. +     * It also provides methods to transform spinner items based on their position +     * relative to the selected item. +     * @param adapter The SpinnerAdapter to use for this Spinner +     */ +    @Override +    public void setAdapter(SpinnerAdapter adapter) { +        if (null != mAdapter) { +            mAdapter.unregisterDataSetObserver(mDataSetObserver); +            resetList(); +        } + +        mAdapter = adapter; + +        mOldSelectedPosition = INVALID_POSITION; +        mOldSelectedRowId = INVALID_ROW_ID; + +        if (mAdapter != null) { +            mOldItemCount = mItemCount; +            mItemCount = mAdapter.getCount(); +            checkFocus(); + +            mDataSetObserver = new AdapterDataSetObserver(); +            mAdapter.registerDataSetObserver(mDataSetObserver); + +            int position = mItemCount > 0 ? 0 : INVALID_POSITION; + +            setSelectedPositionInt(position); +            setNextSelectedPositionInt(position); + +            if (mItemCount == 0) { +                // Nothing selected +                checkSelectionChanged(); +            } + +        } else { +            checkFocus(); +            resetList(); +            // Nothing selected +            checkSelectionChanged(); +        } + +        requestLayout(); +    } + +    /** +     * Clear out all children from the list +     */ +    void resetList() { +        mDataChanged = false; +        mNeedSync = false; + +        removeAllViewsInLayout(); +        mOldSelectedPosition = INVALID_POSITION; +        mOldSelectedRowId = INVALID_ROW_ID; + +        setSelectedPositionInt(INVALID_POSITION); +        setNextSelectedPositionInt(INVALID_POSITION); +        invalidate(); +    } + +    /** +     * @see android.view.View#measure(int, int) +     * +     * Figure out the dimensions of this Spinner. The width comes from +     * the widthMeasureSpec as Spinnners can't have their width set to +     * UNSPECIFIED. The height is based on the height of the selected item +     * plus padding. +     */ +    @Override +    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        int widthMode = MeasureSpec.getMode(widthMeasureSpec); +        int widthSize; +        int heightSize; + +        final int mPaddingLeft = getPaddingLeft(); +        final int mPaddingTop = getPaddingTop(); +        final int mPaddingRight = getPaddingRight(); +        final int mPaddingBottom = getPaddingBottom(); + +        mSpinnerPadding.left = mPaddingLeft > mSelectionLeftPadding ? mPaddingLeft +                : mSelectionLeftPadding; +        mSpinnerPadding.top = mPaddingTop > mSelectionTopPadding ? mPaddingTop +                : mSelectionTopPadding; +        mSpinnerPadding.right = mPaddingRight > mSelectionRightPadding ? mPaddingRight +                : mSelectionRightPadding; +        mSpinnerPadding.bottom = mPaddingBottom > mSelectionBottomPadding ? mPaddingBottom +                : mSelectionBottomPadding; + +        if (mDataChanged) { +            handleDataChanged(); +        } + +        int preferredHeight = 0; +        int preferredWidth = 0; +        boolean needsMeasuring = true; + +        int selectedPosition = getSelectedItemPosition(); +        if (selectedPosition >= 0 && mAdapter != null && selectedPosition < mAdapter.getCount()) { +            // Try looking in the recycler. (Maybe we were measured once already) +            View view = mRecycler.get(selectedPosition); +            if (view == null) { +                // Make a new one +                view = mAdapter.getView(selectedPosition, null, this); +            } + +            if (view != null) { +                // Put in recycler for re-measuring and/or layout +                mRecycler.put(selectedPosition, view); +            } + +            if (view != null) { +                if (view.getLayoutParams() == null) { +                    mBlockLayoutRequests = true; +                    view.setLayoutParams(generateDefaultLayoutParams()); +                    mBlockLayoutRequests = false; +                } +                measureChild(view, widthMeasureSpec, heightMeasureSpec); + +                preferredHeight = getChildHeight(view) + mSpinnerPadding.top + mSpinnerPadding.bottom; +                preferredWidth = getChildWidth(view) + mSpinnerPadding.left + mSpinnerPadding.right; + +                needsMeasuring = false; +            } +        } + +        if (needsMeasuring) { +            // No views -- just use padding +            preferredHeight = mSpinnerPadding.top + mSpinnerPadding.bottom; +            if (widthMode == MeasureSpec.UNSPECIFIED) { +                preferredWidth = mSpinnerPadding.left + mSpinnerPadding.right; +            } +        } + +        preferredHeight = Math.max(preferredHeight, getSuggestedMinimumHeight()); +        preferredWidth = Math.max(preferredWidth, getSuggestedMinimumWidth()); + +        if (IS_HONEYCOMB) { +            heightSize = resolveSizeAndState(preferredHeight, heightMeasureSpec, 0); +            widthSize = resolveSizeAndState(preferredWidth, widthMeasureSpec, 0); +        } else { +            heightSize = resolveSize(preferredHeight, heightMeasureSpec); +            widthSize = resolveSize(preferredWidth, widthMeasureSpec); +        } + +        setMeasuredDimension(widthSize, heightSize); +        mHeightMeasureSpec = heightMeasureSpec; +        mWidthMeasureSpec = widthMeasureSpec; +    } + +    int getChildHeight(View child) { +        return child.getMeasuredHeight(); +    } + +    int getChildWidth(View child) { +        return child.getMeasuredWidth(); +    } + +    @Override +    protected ViewGroup.LayoutParams generateDefaultLayoutParams() { +        return new ViewGroup.LayoutParams( +                ViewGroup.LayoutParams.MATCH_PARENT, +                ViewGroup.LayoutParams.WRAP_CONTENT); +    } + +    void recycleAllViews() { +        final int childCount = getChildCount(); +        final IcsAbsSpinner.RecycleBin recycleBin = mRecycler; +        final int position = mFirstPosition; + +        // All views go in recycler +        for (int i = 0; i < childCount; i++) { +            View v = getChildAt(i); +            int index = position + i; +            recycleBin.put(index, v); +        } +    } + +    /** +     * Jump directly to a specific item in the adapter data. +     */ +    public void setSelection(int position, boolean animate) { +        // Animate only if requested position is already on screen somewhere +        boolean shouldAnimate = animate && mFirstPosition <= position && +                position <= mFirstPosition + getChildCount() - 1; +        setSelectionInt(position, shouldAnimate); +    } + +    @Override +    public void setSelection(int position) { +        setNextSelectedPositionInt(position); +        requestLayout(); +        invalidate(); +    } + + +    /** +     * Makes the item at the supplied position selected. +     * +     * @param position Position to select +     * @param animate Should the transition be animated +     * +     */ +    void setSelectionInt(int position, boolean animate) { +        if (position != mOldSelectedPosition) { +            mBlockLayoutRequests = true; +            int delta  = position - mSelectedPosition; +            setNextSelectedPositionInt(position); +            layout(delta, animate); +            mBlockLayoutRequests = false; +        } +    } + +    abstract void layout(int delta, boolean animate); + +    @Override +    public View getSelectedView() { +        if (mItemCount > 0 && mSelectedPosition >= 0) { +            return getChildAt(mSelectedPosition - mFirstPosition); +        } else { +            return null; +        } +    } + +    /** +     * Override to prevent spamming ourselves with layout requests +     * as we place views +     * +     * @see android.view.View#requestLayout() +     */ +    @Override +    public void requestLayout() { +        if (!mBlockLayoutRequests) { +            super.requestLayout(); +        } +    } + +    @Override +    public SpinnerAdapter getAdapter() { +        return mAdapter; +    } + +    @Override +    public int getCount() { +        return mItemCount; +    } + +    /** +     * Maps a point to a position in the list. +     * +     * @param x X in local coordinate +     * @param y Y in local coordinate +     * @return The position of the item which contains the specified point, or +     *         {@link #INVALID_POSITION} if the point does not intersect an item. +     */ +    public int pointToPosition(int x, int y) { +        Rect frame = mTouchFrame; +        if (frame == null) { +            mTouchFrame = new Rect(); +            frame = mTouchFrame; +        } + +        final int count = getChildCount(); +        for (int i = count - 1; i >= 0; i--) { +            View child = getChildAt(i); +            if (child.getVisibility() == View.VISIBLE) { +                child.getHitRect(frame); +                if (frame.contains(x, y)) { +                    return mFirstPosition + i; +                } +            } +        } +        return INVALID_POSITION; +    } + +    static class SavedState extends BaseSavedState { +        long selectedId; +        int position; + +        /** +         * Constructor called from {@link AbsSpinner#onSaveInstanceState()} +         */ +        SavedState(Parcelable superState) { +            super(superState); +        } + +        /** +         * Constructor called from {@link #CREATOR} +         */ +        private SavedState(Parcel in) { +            super(in); +            selectedId = in.readLong(); +            position = in.readInt(); +        } + +        @Override +        public void writeToParcel(Parcel out, int flags) { +            super.writeToParcel(out, flags); +            out.writeLong(selectedId); +            out.writeInt(position); +        } + +        @Override +        public String toString() { +            return "AbsSpinner.SavedState{" +                    + Integer.toHexString(System.identityHashCode(this)) +                    + " selectedId=" + selectedId +                    + " position=" + position + "}"; +        } + +        public static final Parcelable.Creator<SavedState> CREATOR +                = new Parcelable.Creator<SavedState>() { +            public SavedState createFromParcel(Parcel in) { +                return new SavedState(in); +            } + +            public SavedState[] newArray(int size) { +                return new SavedState[size]; +            } +        }; +    } + +    @Override +    public Parcelable onSaveInstanceState() { +        Parcelable superState = super.onSaveInstanceState(); +        SavedState ss = new SavedState(superState); +        ss.selectedId = getSelectedItemId(); +        if (ss.selectedId >= 0) { +            ss.position = getSelectedItemPosition(); +        } else { +            ss.position = INVALID_POSITION; +        } +        return ss; +    } + +    @Override +    public void onRestoreInstanceState(Parcelable state) { +        SavedState ss = (SavedState) state; + +        super.onRestoreInstanceState(ss.getSuperState()); + +        if (ss.selectedId >= 0) { +            mDataChanged = true; +            mNeedSync = true; +            mSyncRowId = ss.selectedId; +            mSyncPosition = ss.position; +            mSyncMode = SYNC_SELECTED_POSITION; +            requestLayout(); +        } +    } + +    class RecycleBin { +        private final SparseArray<View> mScrapHeap = new SparseArray<View>(); + +        public void put(int position, View v) { +            mScrapHeap.put(position, v); +        } + +        View get(int position) { +            // System.out.print("Looking for " + position); +            View result = mScrapHeap.get(position); +            if (result != null) { +                // System.out.println(" HIT"); +                mScrapHeap.delete(position); +            } else { +                // System.out.println(" MISS"); +            } +            return result; +        } + +        void clear() { +            final SparseArray<View> scrapHeap = mScrapHeap; +            final int count = scrapHeap.size(); +            for (int i = 0; i < count; i++) { +                final View view = scrapHeap.valueAt(i); +                if (view != null) { +                    removeDetachedView(view, true); +                } +            } +            scrapHeap.clear(); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAdapterView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAdapterView.java new file mode 100644 index 000000000..c786dc5c1 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAdapterView.java @@ -0,0 +1,1160 @@ +/* + * Copyright (C) 2006 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.actionbarsherlock.internal.widget; + +import android.content.Context; +import android.database.DataSetObserver; +import android.os.Parcelable; +import android.os.SystemClock; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.view.ContextMenu; +import android.view.SoundEffectConstants; +import android.view.View; +import android.view.ViewDebug; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.Adapter; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ListView; + + +/** + * An AdapterView is a view whose children are determined by an {@link Adapter}. + * + * <p> + * See {@link ListView}, {@link GridView}, {@link Spinner} and + *      {@link Gallery} for commonly used subclasses of AdapterView. + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about using AdapterView, read the + * <a href="{@docRoot}guide/topics/ui/binding.html">Binding to Data with AdapterView</a> + * developer guide.</p></div> + */ +public abstract class IcsAdapterView<T extends Adapter> extends ViewGroup { + +    /** +     * The item view type returned by {@link Adapter#getItemViewType(int)} when +     * the adapter does not want the item's view recycled. +     */ +    public static final int ITEM_VIEW_TYPE_IGNORE = -1; + +    /** +     * The item view type returned by {@link Adapter#getItemViewType(int)} when +     * the item is a header or footer. +     */ +    public static final int ITEM_VIEW_TYPE_HEADER_OR_FOOTER = -2; + +    /** +     * The position of the first child displayed +     */ +    @ViewDebug.ExportedProperty(category = "scrolling") +    int mFirstPosition = 0; + +    /** +     * The offset in pixels from the top of the AdapterView to the top +     * of the view to select during the next layout. +     */ +    int mSpecificTop; + +    /** +     * Position from which to start looking for mSyncRowId +     */ +    int mSyncPosition; + +    /** +     * Row id to look for when data has changed +     */ +    long mSyncRowId = INVALID_ROW_ID; + +    /** +     * Height of the view when mSyncPosition and mSyncRowId where set +     */ +    long mSyncHeight; + +    /** +     * True if we need to sync to mSyncRowId +     */ +    boolean mNeedSync = false; + +    /** +     * Indicates whether to sync based on the selection or position. Possible +     * values are {@link #SYNC_SELECTED_POSITION} or +     * {@link #SYNC_FIRST_POSITION}. +     */ +    int mSyncMode; + +    /** +     * Our height after the last layout +     */ +    private int mLayoutHeight; + +    /** +     * Sync based on the selected child +     */ +    static final int SYNC_SELECTED_POSITION = 0; + +    /** +     * Sync based on the first child displayed +     */ +    static final int SYNC_FIRST_POSITION = 1; + +    /** +     * Maximum amount of time to spend in {@link #findSyncPosition()} +     */ +    static final int SYNC_MAX_DURATION_MILLIS = 100; + +    /** +     * Indicates that this view is currently being laid out. +     */ +    boolean mInLayout = false; + +    /** +     * The listener that receives notifications when an item is selected. +     */ +    OnItemSelectedListener mOnItemSelectedListener; + +    /** +     * The listener that receives notifications when an item is clicked. +     */ +    OnItemClickListener mOnItemClickListener; + +    /** +     * The listener that receives notifications when an item is long clicked. +     */ +    OnItemLongClickListener mOnItemLongClickListener; + +    /** +     * True if the data has changed since the last layout +     */ +    boolean mDataChanged; + +    /** +     * The position within the adapter's data set of the item to select +     * during the next layout. +     */ +    @ViewDebug.ExportedProperty(category = "list") +    int mNextSelectedPosition = INVALID_POSITION; + +    /** +     * The item id of the item to select during the next layout. +     */ +    long mNextSelectedRowId = INVALID_ROW_ID; + +    /** +     * The position within the adapter's data set of the currently selected item. +     */ +    @ViewDebug.ExportedProperty(category = "list") +    int mSelectedPosition = INVALID_POSITION; + +    /** +     * The item id of the currently selected item. +     */ +    long mSelectedRowId = INVALID_ROW_ID; + +    /** +     * View to show if there are no items to show. +     */ +    private View mEmptyView; + +    /** +     * The number of items in the current adapter. +     */ +    @ViewDebug.ExportedProperty(category = "list") +    int mItemCount; + +    /** +     * The number of items in the adapter before a data changed event occurred. +     */ +    int mOldItemCount; + +    /** +     * Represents an invalid position. All valid positions are in the range 0 to 1 less than the +     * number of items in the current adapter. +     */ +    public static final int INVALID_POSITION = -1; + +    /** +     * Represents an empty or invalid row id +     */ +    public static final long INVALID_ROW_ID = Long.MIN_VALUE; + +    /** +     * The last selected position we used when notifying +     */ +    int mOldSelectedPosition = INVALID_POSITION; + +    /** +     * The id of the last selected position we used when notifying +     */ +    long mOldSelectedRowId = INVALID_ROW_ID; + +    /** +     * Indicates what focusable state is requested when calling setFocusable(). +     * In addition to this, this view has other criteria for actually +     * determining the focusable state (such as whether its empty or the text +     * filter is shown). +     * +     * @see #setFocusable(boolean) +     * @see #checkFocus() +     */ +    private boolean mDesiredFocusableState; +    private boolean mDesiredFocusableInTouchModeState; + +    private SelectionNotifier mSelectionNotifier; +    /** +     * When set to true, calls to requestLayout() will not propagate up the parent hierarchy. +     * This is used to layout the children during a layout pass. +     */ +    boolean mBlockLayoutRequests = false; + +    public IcsAdapterView(Context context) { +        super(context); +    } + +    public IcsAdapterView(Context context, AttributeSet attrs) { +        super(context, attrs); +    } + +    public IcsAdapterView(Context context, AttributeSet attrs, int defStyle) { +        super(context, attrs, defStyle); +    } + +    /** +     * Register a callback to be invoked when an item in this AdapterView has +     * been clicked. +     * +     * @param listener The callback that will be invoked. +     */ +    public void setOnItemClickListener(OnItemClickListener listener) { +        mOnItemClickListener = listener; +    } + +    /** +     * @return The callback to be invoked with an item in this AdapterView has +     *         been clicked, or null id no callback has been set. +     */ +    public final OnItemClickListener getOnItemClickListener() { +        return mOnItemClickListener; +    } + +    /** +     * Call the OnItemClickListener, if it is defined. +     * +     * @param view The view within the AdapterView that was clicked. +     * @param position The position of the view in the adapter. +     * @param id The row id of the item that was clicked. +     * @return True if there was an assigned OnItemClickListener that was +     *         called, false otherwise is returned. +     */ +    public boolean performItemClick(View view, int position, long id) { +        if (mOnItemClickListener != null) { +            playSoundEffect(SoundEffectConstants.CLICK); +            if (view != null) { +                view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); +            } +            mOnItemClickListener.onItemClick(/*this*/null, view, position, id); +            return true; +        } + +        return false; +    } + +    /** +     * Interface definition for a callback to be invoked when an item in this +     * view has been clicked and held. +     */ +    public interface OnItemLongClickListener { +        /** +         * Callback method to be invoked when an item in this view has been +         * clicked and held. +         * +         * Implementers can call getItemAtPosition(position) if they need to access +         * the data associated with the selected item. +         * +         * @param parent The AbsListView where the click happened +         * @param view The view within the AbsListView that was clicked +         * @param position The position of the view in the list +         * @param id The row id of the item that was clicked +         * +         * @return true if the callback consumed the long click, false otherwise +         */ +        boolean onItemLongClick(IcsAdapterView<?> parent, View view, int position, long id); +    } + + +    /** +     * Register a callback to be invoked when an item in this AdapterView has +     * been clicked and held +     * +     * @param listener The callback that will run +     */ +    public void setOnItemLongClickListener(OnItemLongClickListener listener) { +        if (!isLongClickable()) { +            setLongClickable(true); +        } +        mOnItemLongClickListener = listener; +    } + +    /** +     * @return The callback to be invoked with an item in this AdapterView has +     *         been clicked and held, or null id no callback as been set. +     */ +    public final OnItemLongClickListener getOnItemLongClickListener() { +        return mOnItemLongClickListener; +    } + +    /** +     * Interface definition for a callback to be invoked when +     * an item in this view has been selected. +     */ +    public interface OnItemSelectedListener { +        /** +         * <p>Callback method to be invoked when an item in this view has been +         * selected. This callback is invoked only when the newly selected +         * position is different from the previously selected position or if +         * there was no selected item.</p> +         * +         * Impelmenters can call getItemAtPosition(position) if they need to access the +         * data associated with the selected item. +         * +         * @param parent The AdapterView where the selection happened +         * @param view The view within the AdapterView that was clicked +         * @param position The position of the view in the adapter +         * @param id The row id of the item that is selected +         */ +        void onItemSelected(IcsAdapterView<?> parent, View view, int position, long id); + +        /** +         * Callback method to be invoked when the selection disappears from this +         * view. The selection can disappear for instance when touch is activated +         * or when the adapter becomes empty. +         * +         * @param parent The AdapterView that now contains no selected item. +         */ +        void onNothingSelected(IcsAdapterView<?> parent); +    } + + +    /** +     * Register a callback to be invoked when an item in this AdapterView has +     * been selected. +     * +     * @param listener The callback that will run +     */ +    public void setOnItemSelectedListener(OnItemSelectedListener listener) { +        mOnItemSelectedListener = listener; +    } + +    public final OnItemSelectedListener getOnItemSelectedListener() { +        return mOnItemSelectedListener; +    } + +    /** +     * Extra menu information provided to the +     * {@link android.view.View.OnCreateContextMenuListener#onCreateContextMenu(ContextMenu, View, ContextMenuInfo) } +     * callback when a context menu is brought up for this AdapterView. +     * +     */ +    public static class AdapterContextMenuInfo implements ContextMenu.ContextMenuInfo { + +        public AdapterContextMenuInfo(View targetView, int position, long id) { +            this.targetView = targetView; +            this.position = position; +            this.id = id; +        } + +        /** +         * The child view for which the context menu is being displayed. This +         * will be one of the children of this AdapterView. +         */ +        public View targetView; + +        /** +         * The position in the adapter for which the context menu is being +         * displayed. +         */ +        public int position; + +        /** +         * The row id of the item for which the context menu is being displayed. +         */ +        public long id; +    } + +    /** +     * Returns the adapter currently associated with this widget. +     * +     * @return The adapter used to provide this view's content. +     */ +    public abstract T getAdapter(); + +    /** +     * Sets the adapter that provides the data and the views to represent the data +     * in this widget. +     * +     * @param adapter The adapter to use to create this view's content. +     */ +    public abstract void setAdapter(T adapter); + +    /** +     * This method is not supported and throws an UnsupportedOperationException when called. +     * +     * @param child Ignored. +     * +     * @throws UnsupportedOperationException Every time this method is invoked. +     */ +    @Override +    public void addView(View child) { +        throw new UnsupportedOperationException("addView(View) is not supported in AdapterView"); +    } + +    /** +     * This method is not supported and throws an UnsupportedOperationException when called. +     * +     * @param child Ignored. +     * @param index Ignored. +     * +     * @throws UnsupportedOperationException Every time this method is invoked. +     */ +    @Override +    public void addView(View child, int index) { +        throw new UnsupportedOperationException("addView(View, int) is not supported in AdapterView"); +    } + +    /** +     * This method is not supported and throws an UnsupportedOperationException when called. +     * +     * @param child Ignored. +     * @param params Ignored. +     * +     * @throws UnsupportedOperationException Every time this method is invoked. +     */ +    @Override +    public void addView(View child, LayoutParams params) { +        throw new UnsupportedOperationException("addView(View, LayoutParams) " +                + "is not supported in AdapterView"); +    } + +    /** +     * This method is not supported and throws an UnsupportedOperationException when called. +     * +     * @param child Ignored. +     * @param index Ignored. +     * @param params Ignored. +     * +     * @throws UnsupportedOperationException Every time this method is invoked. +     */ +    @Override +    public void addView(View child, int index, LayoutParams params) { +        throw new UnsupportedOperationException("addView(View, int, LayoutParams) " +                + "is not supported in AdapterView"); +    } + +    /** +     * This method is not supported and throws an UnsupportedOperationException when called. +     * +     * @param child Ignored. +     * +     * @throws UnsupportedOperationException Every time this method is invoked. +     */ +    @Override +    public void removeView(View child) { +        throw new UnsupportedOperationException("removeView(View) is not supported in AdapterView"); +    } + +    /** +     * This method is not supported and throws an UnsupportedOperationException when called. +     * +     * @param index Ignored. +     * +     * @throws UnsupportedOperationException Every time this method is invoked. +     */ +    @Override +    public void removeViewAt(int index) { +        throw new UnsupportedOperationException("removeViewAt(int) is not supported in AdapterView"); +    } + +    /** +     * This method is not supported and throws an UnsupportedOperationException when called. +     * +     * @throws UnsupportedOperationException Every time this method is invoked. +     */ +    @Override +    public void removeAllViews() { +        throw new UnsupportedOperationException("removeAllViews() is not supported in AdapterView"); +    } + +    @Override +    protected void onLayout(boolean changed, int left, int top, int right, int bottom) { +        mLayoutHeight = getHeight(); +    } + +    /** +     * Return the position of the currently selected item within the adapter's data set +     * +     * @return int Position (starting at 0), or {@link #INVALID_POSITION} if there is nothing selected. +     */ +    @ViewDebug.CapturedViewProperty +    public int getSelectedItemPosition() { +        return mNextSelectedPosition; +    } + +    /** +     * @return The id corresponding to the currently selected item, or {@link #INVALID_ROW_ID} +     * if nothing is selected. +     */ +    @ViewDebug.CapturedViewProperty +    public long getSelectedItemId() { +        return mNextSelectedRowId; +    } + +    /** +     * @return The view corresponding to the currently selected item, or null +     * if nothing is selected +     */ +    public abstract View getSelectedView(); + +    /** +     * @return The data corresponding to the currently selected item, or +     * null if there is nothing selected. +     */ +    public Object getSelectedItem() { +        T adapter = getAdapter(); +        int selection = getSelectedItemPosition(); +        if (adapter != null && adapter.getCount() > 0 && selection >= 0) { +            return adapter.getItem(selection); +        } else { +            return null; +        } +    } + +    /** +     * @return The number of items owned by the Adapter associated with this +     *         AdapterView. (This is the number of data items, which may be +     *         larger than the number of visible views.) +     */ +    @ViewDebug.CapturedViewProperty +    public int getCount() { +        return mItemCount; +    } + +    /** +     * Get the position within the adapter's data set for the view, where view is a an adapter item +     * or a descendant of an adapter item. +     * +     * @param view an adapter item, or a descendant of an adapter item. This must be visible in this +     *        AdapterView at the time of the call. +     * @return the position within the adapter's data set of the view, or {@link #INVALID_POSITION} +     *         if the view does not correspond to a list item (or it is not currently visible). +     */ +    public int getPositionForView(View view) { +        View listItem = view; +        try { +            View v; +            while (!(v = (View) listItem.getParent()).equals(this)) { +                listItem = v; +            } +        } catch (ClassCastException e) { +            // We made it up to the window without find this list view +            return INVALID_POSITION; +        } + +        // Search the children for the list item +        final int childCount = getChildCount(); +        for (int i = 0; i < childCount; i++) { +            if (getChildAt(i).equals(listItem)) { +                return mFirstPosition + i; +            } +        } + +        // Child not found! +        return INVALID_POSITION; +    } + +    /** +     * Returns the position within the adapter's data set for the first item +     * displayed on screen. +     * +     * @return The position within the adapter's data set +     */ +    public int getFirstVisiblePosition() { +        return mFirstPosition; +    } + +    /** +     * Returns the position within the adapter's data set for the last item +     * displayed on screen. +     * +     * @return The position within the adapter's data set +     */ +    public int getLastVisiblePosition() { +        return mFirstPosition + getChildCount() - 1; +    } + +    /** +     * Sets the currently selected item. To support accessibility subclasses that +     * override this method must invoke the overriden super method first. +     * +     * @param position Index (starting at 0) of the data item to be selected. +     */ +    public abstract void setSelection(int position); + +    /** +     * Sets the view to show if the adapter is empty +     */ +    public void setEmptyView(View emptyView) { +        mEmptyView = emptyView; + +        final T adapter = getAdapter(); +        final boolean empty = ((adapter == null) || adapter.isEmpty()); +        updateEmptyStatus(empty); +    } + +    /** +     * When the current adapter is empty, the AdapterView can display a special view +     * call the empty view. The empty view is used to provide feedback to the user +     * that no data is available in this AdapterView. +     * +     * @return The view to show if the adapter is empty. +     */ +    public View getEmptyView() { +        return mEmptyView; +    } + +    /** +     * Indicates whether this view is in filter mode. Filter mode can for instance +     * be enabled by a user when typing on the keyboard. +     * +     * @return True if the view is in filter mode, false otherwise. +     */ +    boolean isInFilterMode() { +        return false; +    } + +    @Override +    public void setFocusable(boolean focusable) { +        final T adapter = getAdapter(); +        final boolean empty = adapter == null || adapter.getCount() == 0; + +        mDesiredFocusableState = focusable; +        if (!focusable) { +            mDesiredFocusableInTouchModeState = false; +        } + +        super.setFocusable(focusable && (!empty || isInFilterMode())); +    } + +    @Override +    public void setFocusableInTouchMode(boolean focusable) { +        final T adapter = getAdapter(); +        final boolean empty = adapter == null || adapter.getCount() == 0; + +        mDesiredFocusableInTouchModeState = focusable; +        if (focusable) { +            mDesiredFocusableState = true; +        } + +        super.setFocusableInTouchMode(focusable && (!empty || isInFilterMode())); +    } + +    void checkFocus() { +        final T adapter = getAdapter(); +        final boolean empty = adapter == null || adapter.getCount() == 0; +        final boolean focusable = !empty || isInFilterMode(); +        // The order in which we set focusable in touch mode/focusable may matter +        // for the client, see View.setFocusableInTouchMode() comments for more +        // details +        super.setFocusableInTouchMode(focusable && mDesiredFocusableInTouchModeState); +        super.setFocusable(focusable && mDesiredFocusableState); +        if (mEmptyView != null) { +            updateEmptyStatus((adapter == null) || adapter.isEmpty()); +        } +    } + +    /** +     * Update the status of the list based on the empty parameter.  If empty is true and +     * we have an empty view, display it.  In all the other cases, make sure that the listview +     * is VISIBLE and that the empty view is GONE (if it's not null). +     */ +    private void updateEmptyStatus(boolean empty) { +        if (isInFilterMode()) { +            empty = false; +        } + +        if (empty) { +            if (mEmptyView != null) { +                mEmptyView.setVisibility(View.VISIBLE); +                setVisibility(View.GONE); +            } else { +                // If the caller just removed our empty view, make sure the list view is visible +                setVisibility(View.VISIBLE); +            } + +            // We are now GONE, so pending layouts will not be dispatched. +            // Force one here to make sure that the state of the list matches +            // the state of the adapter. +            if (mDataChanged) { +                this.onLayout(false, getLeft(), getTop(), getRight(), getBottom()); +            } +        } else { +            if (mEmptyView != null) mEmptyView.setVisibility(View.GONE); +            setVisibility(View.VISIBLE); +        } +    } + +    /** +     * Gets the data associated with the specified position in the list. +     * +     * @param position Which data to get +     * @return The data associated with the specified position in the list +     */ +    public Object getItemAtPosition(int position) { +        T adapter = getAdapter(); +        return (adapter == null || position < 0) ? null : adapter.getItem(position); +    } + +    public long getItemIdAtPosition(int position) { +        T adapter = getAdapter(); +        return (adapter == null || position < 0) ? INVALID_ROW_ID : adapter.getItemId(position); +    } + +    @Override +    public void setOnClickListener(OnClickListener l) { +        throw new RuntimeException("Don't call setOnClickListener for an AdapterView. " +                + "You probably want setOnItemClickListener instead"); +    } + +    /** +     * Override to prevent freezing of any views created by the adapter. +     */ +    @Override +    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { +        dispatchFreezeSelfOnly(container); +    } + +    /** +     * Override to prevent thawing of any views created by the adapter. +     */ +    @Override +    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { +        dispatchThawSelfOnly(container); +    } + +    class AdapterDataSetObserver extends DataSetObserver { + +        private Parcelable mInstanceState = null; + +        @Override +        public void onChanged() { +            mDataChanged = true; +            mOldItemCount = mItemCount; +            mItemCount = getAdapter().getCount(); + +            // Detect the case where a cursor that was previously invalidated has +            // been repopulated with new data. +            if (IcsAdapterView.this.getAdapter().hasStableIds() && mInstanceState != null +                    && mOldItemCount == 0 && mItemCount > 0) { +                IcsAdapterView.this.onRestoreInstanceState(mInstanceState); +                mInstanceState = null; +            } else { +                rememberSyncState(); +            } +            checkFocus(); +            requestLayout(); +        } + +        @Override +        public void onInvalidated() { +            mDataChanged = true; + +            if (IcsAdapterView.this.getAdapter().hasStableIds()) { +                // Remember the current state for the case where our hosting activity is being +                // stopped and later restarted +                mInstanceState = IcsAdapterView.this.onSaveInstanceState(); +            } + +            // Data is invalid so we should reset our state +            mOldItemCount = mItemCount; +            mItemCount = 0; +            mSelectedPosition = INVALID_POSITION; +            mSelectedRowId = INVALID_ROW_ID; +            mNextSelectedPosition = INVALID_POSITION; +            mNextSelectedRowId = INVALID_ROW_ID; +            mNeedSync = false; + +            checkFocus(); +            requestLayout(); +        } + +        public void clearSavedState() { +            mInstanceState = null; +        } +    } + +    @Override +    protected void onDetachedFromWindow() { +        super.onDetachedFromWindow(); +        removeCallbacks(mSelectionNotifier); +    } + +    private class SelectionNotifier implements Runnable { +        public void run() { +            if (mDataChanged) { +                // Data has changed between when this SelectionNotifier +                // was posted and now. We need to wait until the AdapterView +                // has been synched to the new data. +                if (getAdapter() != null) { +                    post(this); +                } +            } else { +                fireOnSelected(); +            } +        } +    } + +    void selectionChanged() { +        if (mOnItemSelectedListener != null) { +            if (mInLayout || mBlockLayoutRequests) { +                // If we are in a layout traversal, defer notification +                // by posting. This ensures that the view tree is +                // in a consistent state and is able to accomodate +                // new layout or invalidate requests. +                if (mSelectionNotifier == null) { +                    mSelectionNotifier = new SelectionNotifier(); +                } +                post(mSelectionNotifier); +            } else { +                fireOnSelected(); +            } +        } + +        // we fire selection events here not in View +        if (mSelectedPosition != ListView.INVALID_POSITION && isShown() && !isInTouchMode()) { +            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); +        } +    } + +    private void fireOnSelected() { +        if (mOnItemSelectedListener == null) +            return; + +        int selection = this.getSelectedItemPosition(); +        if (selection >= 0) { +            View v = getSelectedView(); +            mOnItemSelectedListener.onItemSelected(this, v, selection, +                    getAdapter().getItemId(selection)); +        } else { +            mOnItemSelectedListener.onNothingSelected(this); +        } +    } + +    @Override +    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { +        View selectedView = getSelectedView(); +        if (selectedView != null && selectedView.getVisibility() == VISIBLE +                && selectedView.dispatchPopulateAccessibilityEvent(event)) { +            return true; +        } +        return false; +    } + +    @Override +    public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { +        if (super.onRequestSendAccessibilityEvent(child, event)) { +            // Add a record for ourselves as well. +            AccessibilityEvent record = AccessibilityEvent.obtain(); +            onInitializeAccessibilityEvent(record); +            // Populate with the text of the requesting child. +            child.dispatchPopulateAccessibilityEvent(record); +            event.appendRecord(record); +            return true; +        } +        return false; +    } + +    @Override +    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { +        super.onInitializeAccessibilityNodeInfo(info); +        info.setScrollable(isScrollableForAccessibility()); +        View selectedView = getSelectedView(); +        if (selectedView != null) { +            info.setEnabled(selectedView.isEnabled()); +        } +    } + +    @Override +    public void onInitializeAccessibilityEvent(AccessibilityEvent event) { +        super.onInitializeAccessibilityEvent(event); +        event.setScrollable(isScrollableForAccessibility()); +        View selectedView = getSelectedView(); +        if (selectedView != null) { +            event.setEnabled(selectedView.isEnabled()); +        } +        event.setCurrentItemIndex(getSelectedItemPosition()); +        event.setFromIndex(getFirstVisiblePosition()); +        event.setToIndex(getLastVisiblePosition()); +        event.setItemCount(getCount()); +    } + +    private boolean isScrollableForAccessibility() { +        T adapter = getAdapter(); +        if (adapter != null) { +            final int itemCount = adapter.getCount(); +            return itemCount > 0 +                && (getFirstVisiblePosition() > 0 || getLastVisiblePosition() < itemCount - 1); +        } +        return false; +    } + +    @Override +    protected boolean canAnimate() { +        return super.canAnimate() && mItemCount > 0; +    } + +    void handleDataChanged() { +        final int count = mItemCount; +        boolean found = false; + +        if (count > 0) { + +            int newPos; + +            // Find the row we are supposed to sync to +            if (mNeedSync) { +                // Update this first, since setNextSelectedPositionInt inspects +                // it +                mNeedSync = false; + +                // See if we can find a position in the new data with the same +                // id as the old selection +                newPos = findSyncPosition(); +                if (newPos >= 0) { +                    // Verify that new selection is selectable +                    int selectablePos = lookForSelectablePosition(newPos, true); +                    if (selectablePos == newPos) { +                        // Same row id is selected +                        setNextSelectedPositionInt(newPos); +                        found = true; +                    } +                } +            } +            if (!found) { +                // Try to use the same position if we can't find matching data +                newPos = getSelectedItemPosition(); + +                // Pin position to the available range +                if (newPos >= count) { +                    newPos = count - 1; +                } +                if (newPos < 0) { +                    newPos = 0; +                } + +                // Make sure we select something selectable -- first look down +                int selectablePos = lookForSelectablePosition(newPos, true); +                if (selectablePos < 0) { +                    // Looking down didn't work -- try looking up +                    selectablePos = lookForSelectablePosition(newPos, false); +                } +                if (selectablePos >= 0) { +                    setNextSelectedPositionInt(selectablePos); +                    checkSelectionChanged(); +                    found = true; +                } +            } +        } +        if (!found) { +            // Nothing is selected +            mSelectedPosition = INVALID_POSITION; +            mSelectedRowId = INVALID_ROW_ID; +            mNextSelectedPosition = INVALID_POSITION; +            mNextSelectedRowId = INVALID_ROW_ID; +            mNeedSync = false; +            checkSelectionChanged(); +        } +    } + +    void checkSelectionChanged() { +        if ((mSelectedPosition != mOldSelectedPosition) || (mSelectedRowId != mOldSelectedRowId)) { +            selectionChanged(); +            mOldSelectedPosition = mSelectedPosition; +            mOldSelectedRowId = mSelectedRowId; +        } +    } + +    /** +     * Searches the adapter for a position matching mSyncRowId. The search starts at mSyncPosition +     * and then alternates between moving up and moving down until 1) we find the right position, or +     * 2) we run out of time, or 3) we have looked at every position +     * +     * @return Position of the row that matches mSyncRowId, or {@link #INVALID_POSITION} if it can't +     *         be found +     */ +    int findSyncPosition() { +        int count = mItemCount; + +        if (count == 0) { +            return INVALID_POSITION; +        } + +        long idToMatch = mSyncRowId; +        int seed = mSyncPosition; + +        // If there isn't a selection don't hunt for it +        if (idToMatch == INVALID_ROW_ID) { +            return INVALID_POSITION; +        } + +        // Pin seed to reasonable values +        seed = Math.max(0, seed); +        seed = Math.min(count - 1, seed); + +        long endTime = SystemClock.uptimeMillis() + SYNC_MAX_DURATION_MILLIS; + +        long rowId; + +        // first position scanned so far +        int first = seed; + +        // last position scanned so far +        int last = seed; + +        // True if we should move down on the next iteration +        boolean next = false; + +        // True when we have looked at the first item in the data +        boolean hitFirst; + +        // True when we have looked at the last item in the data +        boolean hitLast; + +        // Get the item ID locally (instead of getItemIdAtPosition), so +        // we need the adapter +        T adapter = getAdapter(); +        if (adapter == null) { +            return INVALID_POSITION; +        } + +        while (SystemClock.uptimeMillis() <= endTime) { +            rowId = adapter.getItemId(seed); +            if (rowId == idToMatch) { +                // Found it! +                return seed; +            } + +            hitLast = last == count - 1; +            hitFirst = first == 0; + +            if (hitLast && hitFirst) { +                // Looked at everything +                break; +            } + +            if (hitFirst || (next && !hitLast)) { +                // Either we hit the top, or we are trying to move down +                last++; +                seed = last; +                // Try going up next time +                next = false; +            } else if (hitLast || (!next && !hitFirst)) { +                // Either we hit the bottom, or we are trying to move up +                first--; +                seed = first; +                // Try going down next time +                next = true; +            } + +        } + +        return INVALID_POSITION; +    } + +    /** +     * Find a position that can be selected (i.e., is not a separator). +     * +     * @param position The starting position to look at. +     * @param lookDown Whether to look down for other positions. +     * @return The next selectable position starting at position and then searching either up or +     *         down. Returns {@link #INVALID_POSITION} if nothing can be found. +     */ +    int lookForSelectablePosition(int position, boolean lookDown) { +        return position; +    } + +    /** +     * Utility to keep mSelectedPosition and mSelectedRowId in sync +     * @param position Our current position +     */ +    void setSelectedPositionInt(int position) { +        mSelectedPosition = position; +        mSelectedRowId = getItemIdAtPosition(position); +    } + +    /** +     * Utility to keep mNextSelectedPosition and mNextSelectedRowId in sync +     * @param position Intended value for mSelectedPosition the next time we go +     * through layout +     */ +    void setNextSelectedPositionInt(int position) { +        mNextSelectedPosition = position; +        mNextSelectedRowId = getItemIdAtPosition(position); +        // If we are trying to sync to the selection, update that too +        if (mNeedSync && mSyncMode == SYNC_SELECTED_POSITION && position >= 0) { +            mSyncPosition = position; +            mSyncRowId = mNextSelectedRowId; +        } +    } + +    /** +     * Remember enough information to restore the screen state when the data has +     * changed. +     * +     */ +    void rememberSyncState() { +        if (getChildCount() > 0) { +            mNeedSync = true; +            mSyncHeight = mLayoutHeight; +            if (mSelectedPosition >= 0) { +                // Sync the selection state +                View v = getChildAt(mSelectedPosition - mFirstPosition); +                mSyncRowId = mNextSelectedRowId; +                mSyncPosition = mNextSelectedPosition; +                if (v != null) { +                    mSpecificTop = v.getTop(); +                } +                mSyncMode = SYNC_SELECTED_POSITION; +            } else { +                // Sync the based on the offset of the first view +                View v = getChildAt(0); +                T adapter = getAdapter(); +                if (mFirstPosition >= 0 && mFirstPosition < adapter.getCount()) { +                    mSyncRowId = adapter.getItemId(mFirstPosition); +                } else { +                    mSyncRowId = NO_ID; +                } +                mSyncPosition = mFirstPosition; +                if (v != null) { +                    mSpecificTop = v.getTop(); +                } +                mSyncMode = SYNC_FIRST_POSITION; +            } +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsColorDrawable.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsColorDrawable.java new file mode 100644 index 000000000..a78b3f71b --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsColorDrawable.java @@ -0,0 +1,41 @@ +package com.actionbarsherlock.internal.widget; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; + +/** + * A version of {@link android.graphics.drawable.ColorDrawable} that respects bounds. + */ +public class IcsColorDrawable extends Drawable { +    private int color; +    private final Paint paint = new Paint(); + +    public IcsColorDrawable(int color) { +        this.color = color; +    } + +    @Override public void draw(Canvas canvas) { +        if ((color >>> 24) != 0) { +            paint.setColor(color); +            canvas.drawRect(getBounds(), paint); +        } +    } + +    @Override +    public void setAlpha(int alpha) { +        if (alpha != (color >>> 24)) { +            color = (color & 0x00FFFFFF) & (alpha << 24); +            invalidateSelf(); +        } +    } + +    @Override public void setColorFilter(ColorFilter colorFilter) { +        //Ignored +    } + +    @Override public int getOpacity() { +        return color >>> 24; +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsLinearLayout.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsLinearLayout.java new file mode 100644 index 000000000..4947c41df --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsLinearLayout.java @@ -0,0 +1,410 @@ +package com.actionbarsherlock.internal.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; + +import com.actionbarsherlock.internal.nineoldandroids.widget.NineLinearLayout; + +/** + * A simple extension of a regular linear layout that supports the divider API + * of Android 4.0+. The dividers are added adjacent to the children by changing + * their layout params. If you need to rely on the margins which fall in the + * same orientation as the layout you should wrap the child in a simple + * {@link android.widget.FrameLayout} so it can receive the margin. + */ +public class IcsLinearLayout extends NineLinearLayout { +    private static final int[] R_styleable_LinearLayout = new int[] { +        /* 0 */ android.R.attr.divider, +        /* 1 */ android.R.attr.measureWithLargestChild, +        /* 2 */ android.R.attr.showDividers, +        /* 3 */ android.R.attr.dividerPadding, +    }; +    private static final int LinearLayout_divider = 0; +    private static final int LinearLayout_measureWithLargestChild = 1; +    private static final int LinearLayout_showDividers = 2; +    private static final int LinearLayout_dividerPadding = 3; + +    /** +     * Don't show any dividers. +     */ +    public static final int SHOW_DIVIDER_NONE = 0; +    /** +     * Show a divider at the beginning of the group. +     */ +    public static final int SHOW_DIVIDER_BEGINNING = 1; +    /** +     * Show dividers between each item in the group. +     */ +    public static final int SHOW_DIVIDER_MIDDLE = 2; +    /** +     * Show a divider at the end of the group. +     */ +    public static final int SHOW_DIVIDER_END = 4; + + +    private Drawable mDivider; +    private int mDividerWidth; +    private int mDividerHeight; +    private int mShowDividers; +    private int mDividerPadding; + +    private boolean mUseLargestChild; + +    public IcsLinearLayout(Context context, AttributeSet attrs) { +        super(context, attrs); + +        TypedArray a = context.obtainStyledAttributes(attrs, /*com.android.internal.R.styleable.*/R_styleable_LinearLayout); + +        setDividerDrawable(a.getDrawable(/*com.android.internal.R.styleable.*/LinearLayout_divider)); +        mShowDividers = a.getInt(/*com.android.internal.R.styleable.*/LinearLayout_showDividers, SHOW_DIVIDER_NONE); +        mDividerPadding = a.getDimensionPixelSize(/*com.android.internal.R.styleable.*/LinearLayout_dividerPadding, 0); +        mUseLargestChild = a.getBoolean(/*com.android.internal.R.styleable.*/LinearLayout_measureWithLargestChild, false); + +        a.recycle(); +    } + +    /** +     * Set how dividers should be shown between items in this layout +     * +     * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING}, +     *                     {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END}, +     *                     or {@link #SHOW_DIVIDER_NONE} to show no dividers. +     */ +    public void setShowDividers(int showDividers) { +        if (showDividers != mShowDividers) { +            requestLayout(); +            invalidate(); //XXX This is required if you are toggling a divider off +        } +        mShowDividers = showDividers; +    } + +    /** +     * @return A flag set indicating how dividers should be shown around items. +     * @see #setShowDividers(int) +     */ +    public int getShowDividers() { +        return mShowDividers; +    } + +    /** +     * Set a drawable to be used as a divider between items. +     * @param divider Drawable that will divide each item. +     * @see #setShowDividers(int) +     */ +    public void setDividerDrawable(Drawable divider) { +        if (divider == mDivider) { +            return; +        } +        mDivider = divider; +        if (divider != null) { +            mDividerWidth = divider.getIntrinsicWidth(); +            mDividerHeight = divider.getIntrinsicHeight(); +        } else { +            mDividerWidth = 0; +            mDividerHeight = 0; +        } +        setWillNotDraw(divider == null); +        requestLayout(); +    } + +    /** +     * Set padding displayed on both ends of dividers. +     * +     * @param padding Padding value in pixels that will be applied to each end +     * +     * @see #setShowDividers(int) +     * @see #setDividerDrawable(Drawable) +     * @see #getDividerPadding() +     */ +    public void setDividerPadding(int padding) { +        mDividerPadding = padding; +    } + +    /** +     * Get the padding size used to inset dividers in pixels +     * +     * @see #setShowDividers(int) +     * @see #setDividerDrawable(Drawable) +     * @see #setDividerPadding(int) +     */ +    public int getDividerPadding() { +        return mDividerPadding; +    } + +    /** +     * Get the width of the current divider drawable. +     * +     * @hide Used internally by framework. +     */ +    public int getDividerWidth() { +        return mDividerWidth; +    } + +    @Override +    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { +        final int index = indexOfChild(child); +        final int orientation = getOrientation(); +        final LayoutParams params = (LayoutParams) child.getLayoutParams(); +        if (hasDividerBeforeChildAt(index)) { +            if (orientation == VERTICAL) { +                //Account for the divider by pushing everything up +                params.topMargin = mDividerHeight; +            } else { +                //Account for the divider by pushing everything left +                params.leftMargin = mDividerWidth; +            } +        } + +        final int count = getChildCount(); +        if (index == count - 1) { +            if (hasDividerBeforeChildAt(count)) { +                if (orientation == VERTICAL) { +                    params.bottomMargin = mDividerHeight; +                } else { +                    params.rightMargin = mDividerWidth; +                } +            } +        } +        super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); +    } + +    @Override +    protected void onDraw(Canvas canvas) { +        if (mDivider != null) { +            if (getOrientation() == VERTICAL) { +                drawDividersVertical(canvas); +            } else { +                drawDividersHorizontal(canvas); +            } +        } +        super.onDraw(canvas); +    } + +    void drawDividersVertical(Canvas canvas) { +        final int count = getChildCount(); +        for (int i = 0; i < count; i++) { +            final View child = getChildAt(i); + +            if (child != null && child.getVisibility() != GONE) { +                if (hasDividerBeforeChildAt(i)) { +                    final LayoutParams lp = (LayoutParams) child.getLayoutParams(); +                    final int top = child.getTop() - lp.topMargin/* - mDividerHeight*/; +                    drawHorizontalDivider(canvas, top); +                } +            } +        } + +        if (hasDividerBeforeChildAt(count)) { +            final View child = getChildAt(count - 1); +            int bottom = 0; +            if (child == null) { +                bottom = getHeight() - getPaddingBottom() - mDividerHeight; +            } else { +                //final LayoutParams lp = (LayoutParams) child.getLayoutParams(); +                bottom = child.getBottom()/* + lp.bottomMargin*/; +            } +            drawHorizontalDivider(canvas, bottom); +        } +    } + +    void drawDividersHorizontal(Canvas canvas) { +        final int count = getChildCount(); +        for (int i = 0; i < count; i++) { +            final View child = getChildAt(i); + +            if (child != null && child.getVisibility() != GONE) { +                if (hasDividerBeforeChildAt(i)) { +                    final LayoutParams lp = (LayoutParams) child.getLayoutParams(); +                    final int left = child.getLeft() - lp.leftMargin/* - mDividerWidth*/; +                    drawVerticalDivider(canvas, left); +                } +            } +        } + +        if (hasDividerBeforeChildAt(count)) { +            final View child = getChildAt(count - 1); +            int right = 0; +            if (child == null) { +                right = getWidth() - getPaddingRight() - mDividerWidth; +            } else { +                //final LayoutParams lp = (LayoutParams) child.getLayoutParams(); +                right = child.getRight()/* + lp.rightMargin*/; +            } +            drawVerticalDivider(canvas, right); +        } +    } + +    void drawHorizontalDivider(Canvas canvas, int top) { +        mDivider.setBounds(getPaddingLeft() + mDividerPadding, top, +                getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight); +        mDivider.draw(canvas); +    } + +    void drawVerticalDivider(Canvas canvas, int left) { +        mDivider.setBounds(left, getPaddingTop() + mDividerPadding, +                left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding); +        mDivider.draw(canvas); +    } + +    /** +     * Determines where to position dividers between children. +     * +     * @param childIndex Index of child to check for preceding divider +     * @return true if there should be a divider before the child at childIndex +     * @hide Pending API consideration. Currently only used internally by the system. +     */ +    protected boolean hasDividerBeforeChildAt(int childIndex) { +        if (childIndex == 0) { +            return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0; +        } else if (childIndex == getChildCount()) { +            return (mShowDividers & SHOW_DIVIDER_END) != 0; +        } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) { +            boolean hasVisibleViewBefore = false; +            for (int i = childIndex - 1; i >= 0; i--) { +                if (getChildAt(i).getVisibility() != GONE) { +                    hasVisibleViewBefore = true; +                    break; +                } +            } +            return hasVisibleViewBefore; +        } +        return false; +    } + +    /** +     * When true, all children with a weight will be considered having +     * the minimum size of the largest child. If false, all children are +     * measured normally. +     * +     * @return True to measure children with a weight using the minimum +     *         size of the largest child, false otherwise. +     * +     * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild +     */ +    public boolean isMeasureWithLargestChildEnabled() { +        return mUseLargestChild; +    } + +    /** +     * When set to true, all children with a weight will be considered having +     * the minimum size of the largest child. If false, all children are +     * measured normally. +     * +     * Disabled by default. +     * +     * @param enabled True to measure children with a weight using the +     *        minimum size of the largest child, false otherwise. +     * +     * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild +     */ +    public void setMeasureWithLargestChildEnabled(boolean enabled) { +        mUseLargestChild = enabled; +    } + +    @Override +    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        super.onMeasure(widthMeasureSpec, heightMeasureSpec); + +        if (mUseLargestChild) { +            final int orientation = getOrientation(); +            switch (orientation) { +                case HORIZONTAL: +                    useLargestChildHorizontal(); +                    break; + +                case VERTICAL: +                    useLargestChildVertical(); +                    break; +            } +        } +    } + +    private void useLargestChildHorizontal() { +        final int childCount = getChildCount(); + +        // Find largest child width +        int largestChildWidth = 0; +        for (int i = 0; i < childCount; i++) { +            final View child = getChildAt(i); +            largestChildWidth = Math.max(child.getMeasuredWidth(), largestChildWidth); +        } + +        int totalWidth = 0; +        // Re-measure childs +        for (int i = 0; i < childCount; i++) { +            final View child = getChildAt(i); + +            if (child == null || child.getVisibility() == View.GONE) { +                continue; +            } + +            final LinearLayout.LayoutParams lp = +                    (LinearLayout.LayoutParams) child.getLayoutParams(); + +            float childExtra = lp.weight; +            if (childExtra > 0) { +                child.measure( +                        MeasureSpec.makeMeasureSpec(largestChildWidth, +                                MeasureSpec.EXACTLY), +                        MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), +                                MeasureSpec.EXACTLY)); +                totalWidth += largestChildWidth; + +            } else { +                totalWidth += child.getMeasuredWidth(); +            } + +            totalWidth += lp.leftMargin + lp.rightMargin; +        } + +        totalWidth += getPaddingLeft() + getPaddingRight(); +        setMeasuredDimension(totalWidth, getMeasuredHeight()); +    } + +    private void useLargestChildVertical() { +        final int childCount = getChildCount(); + +        // Find largest child width +        int largestChildHeight = 0; +        for (int i = 0; i < childCount; i++) { +            final View child = getChildAt(i); +            largestChildHeight = Math.max(child.getMeasuredHeight(), largestChildHeight); +        } + +        int totalHeight = 0; +        // Re-measure childs +        for (int i = 0; i < childCount; i++) { +            final View child = getChildAt(i); + +            if (child == null || child.getVisibility() == View.GONE) { +                continue; +            } + +            final LinearLayout.LayoutParams lp = +                    (LinearLayout.LayoutParams) child.getLayoutParams(); + +            float childExtra = lp.weight; +            if (childExtra > 0) { +                child.measure( +                        MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(), +                                MeasureSpec.EXACTLY), +                        MeasureSpec.makeMeasureSpec(largestChildHeight, +                                MeasureSpec.EXACTLY)); +                totalHeight += largestChildHeight; + +            } else { +                totalHeight += child.getMeasuredHeight(); +            } + +            totalHeight += lp.leftMargin + lp.rightMargin; +        } + +        totalHeight += getPaddingLeft() + getPaddingRight(); +        setMeasuredDimension(getMeasuredWidth(), totalHeight); +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsListPopupWindow.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsListPopupWindow.java new file mode 100644 index 000000000..d13c6cea9 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsListPopupWindow.java @@ -0,0 +1,644 @@ +package com.actionbarsherlock.internal.widget; + +import com.actionbarsherlock.R; + +import android.content.Context; +import android.content.res.Resources; +import android.database.DataSetObserver; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.ContextThemeWrapper; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.MeasureSpec; +import android.view.View.OnTouchListener; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.LinearLayout; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.PopupWindow; + +/** + * A proxy between pre- and post-Honeycomb implementations of this class. + */ +public class IcsListPopupWindow { +    /** +     * This value controls the length of time that the user +     * must leave a pointer down without scrolling to expand +     * the autocomplete dropdown list to cover the IME. +     */ +    private static final int EXPAND_LIST_TIMEOUT = 250; + +    private Context mContext; +    private PopupWindow mPopup; +    private ListAdapter mAdapter; +    private DropDownListView mDropDownList; + +    private int mDropDownHeight = ViewGroup.LayoutParams.WRAP_CONTENT; +    private int mDropDownWidth = ViewGroup.LayoutParams.WRAP_CONTENT; +    private int mDropDownHorizontalOffset; +    private int mDropDownVerticalOffset; +    private boolean mDropDownVerticalOffsetSet; + +    private int mListItemExpandMaximum = Integer.MAX_VALUE; + +    private View mPromptView; +    private int mPromptPosition = POSITION_PROMPT_ABOVE; + +    private DataSetObserver mObserver; + +    private View mDropDownAnchorView; + +    private Drawable mDropDownListHighlight; + +    private AdapterView.OnItemClickListener mItemClickListener; +    private AdapterView.OnItemSelectedListener mItemSelectedListener; + +    private final ResizePopupRunnable mResizePopupRunnable = new ResizePopupRunnable(); +    private final PopupTouchInterceptor mTouchInterceptor = new PopupTouchInterceptor(); +    private final PopupScrollListener mScrollListener = new PopupScrollListener(); +    private final ListSelectorHider mHideSelector = new ListSelectorHider(); + +    private Handler mHandler = new Handler(); + +    private Rect mTempRect = new Rect(); + +    private boolean mModal; + +    public static final int POSITION_PROMPT_ABOVE = 0; +    public static final int POSITION_PROMPT_BELOW = 1; + +    public IcsListPopupWindow(Context context) { +        this(context, null, R.attr.listPopupWindowStyle); +    } + +    public IcsListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr) { +        mContext = context; +        mPopup = new PopupWindow(context, attrs, defStyleAttr); +        mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); +    } + +    public IcsListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { +        mContext = context; +        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { +            Context wrapped = new ContextThemeWrapper(context, defStyleRes); +            mPopup = new PopupWindow(wrapped, attrs, defStyleAttr); +        } else { +            mPopup = new PopupWindow(context, attrs, defStyleAttr, defStyleRes); +        } +        mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); +    } + +    public void setAdapter(ListAdapter adapter) { +        if (mObserver == null) { +            mObserver = new PopupDataSetObserver(); +        } else if (mAdapter != null) { +            mAdapter.unregisterDataSetObserver(mObserver); +        } +        mAdapter = adapter; +        if (mAdapter != null) { +            adapter.registerDataSetObserver(mObserver); +        } + +        if (mDropDownList != null) { +            mDropDownList.setAdapter(mAdapter); +        } +    } + +    public void setPromptPosition(int position) { +        mPromptPosition = position; +    } + +    public void setModal(boolean modal) { +        mModal = true; +        mPopup.setFocusable(modal); +    } + +    public void setBackgroundDrawable(Drawable d) { +        mPopup.setBackgroundDrawable(d); +    } + +    public void setAnchorView(View anchor) { +        mDropDownAnchorView = anchor; +    } + +    public void setHorizontalOffset(int offset) { +        mDropDownHorizontalOffset = offset; +    } + +    public void setVerticalOffset(int offset) { +        mDropDownVerticalOffset = offset; +        mDropDownVerticalOffsetSet = true; +    } + +    public void setContentWidth(int width) { +        Drawable popupBackground = mPopup.getBackground(); +        if (popupBackground != null) { +            popupBackground.getPadding(mTempRect); +            mDropDownWidth = mTempRect.left + mTempRect.right + width; +        } else { +            mDropDownWidth = width; +        } +    } + +    public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) { +        mItemClickListener = clickListener; +    } + +    public void show() { +        int height = buildDropDown(); + +        int widthSpec = 0; +        int heightSpec = 0; + +        boolean noInputMethod = isInputMethodNotNeeded(); +        //XXX mPopup.setAllowScrollingAnchorParent(!noInputMethod); + +        if (mPopup.isShowing()) { +            if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) { +                // The call to PopupWindow's update method below can accept -1 for any +                // value you do not want to update. +                widthSpec = -1; +            } else if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) { +                widthSpec = mDropDownAnchorView.getWidth(); +            } else { +                widthSpec = mDropDownWidth; +            } + +            if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) { +                // The call to PopupWindow's update method below can accept -1 for any +                // value you do not want to update. +                heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT; +                if (noInputMethod) { +                    mPopup.setWindowLayoutMode( +                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ? +                                    ViewGroup.LayoutParams.MATCH_PARENT : 0, 0); +                } else { +                    mPopup.setWindowLayoutMode( +                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ? +                                    ViewGroup.LayoutParams.MATCH_PARENT : 0, +                            ViewGroup.LayoutParams.MATCH_PARENT); +                } +            } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { +                heightSpec = height; +            } else { +                heightSpec = mDropDownHeight; +            } + +            mPopup.setOutsideTouchable(true); + +            mPopup.update(mDropDownAnchorView, mDropDownHorizontalOffset, +                    mDropDownVerticalOffset, widthSpec, heightSpec); +        } else { +            if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) { +                widthSpec = ViewGroup.LayoutParams.MATCH_PARENT; +            } else { +                if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) { +                    mPopup.setWidth(mDropDownAnchorView.getWidth()); +                } else { +                    mPopup.setWidth(mDropDownWidth); +                } +            } + +            if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) { +                heightSpec = ViewGroup.LayoutParams.MATCH_PARENT; +            } else { +                if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { +                    mPopup.setHeight(height); +                } else { +                    mPopup.setHeight(mDropDownHeight); +                } +            } + +            mPopup.setWindowLayoutMode(widthSpec, heightSpec); +            //XXX mPopup.setClipToScreenEnabled(true); + +            // use outside touchable to dismiss drop down when touching outside of it, so +            // only set this if the dropdown is not always visible +            mPopup.setOutsideTouchable(true); +            mPopup.setTouchInterceptor(mTouchInterceptor); +            mPopup.showAsDropDown(mDropDownAnchorView, +                    mDropDownHorizontalOffset, mDropDownVerticalOffset); +            mDropDownList.setSelection(ListView.INVALID_POSITION); + +            if (!mModal || mDropDownList.isInTouchMode()) { +                clearListSelection(); +            } +            if (!mModal) { +                mHandler.post(mHideSelector); +            } +        } +    } + +    public void dismiss() { +        mPopup.dismiss(); +        if (mPromptView != null) { +            final ViewParent parent = mPromptView.getParent(); +            if (parent instanceof ViewGroup) { +                final ViewGroup group = (ViewGroup) parent; +                group.removeView(mPromptView); +            } +        } +        mPopup.setContentView(null); +        mDropDownList = null; +        mHandler.removeCallbacks(mResizePopupRunnable); +    } + +    public void setOnDismissListener(PopupWindow.OnDismissListener listener) { +        mPopup.setOnDismissListener(listener); +    } + +    public void setInputMethodMode(int mode) { +        mPopup.setInputMethodMode(mode); +    } + +    public void clearListSelection() { +        final DropDownListView list = mDropDownList; +        if (list != null) { +            // WARNING: Please read the comment where mListSelectionHidden is declared +            list.mListSelectionHidden = true; +            //XXX list.hideSelector(); +            list.requestLayout(); +        } +    } + +    public boolean isShowing() { +        return mPopup.isShowing(); +    } + +    private boolean isInputMethodNotNeeded() { +        return mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED; +    } + +    public ListView getListView() { +        return mDropDownList; +    } + +    private int buildDropDown() { +        ViewGroup dropDownView; +        int otherHeights = 0; + +        if (mDropDownList == null) { +            Context context = mContext; + +            mDropDownList = new DropDownListView(context, !mModal); +            if (mDropDownListHighlight != null) { +                mDropDownList.setSelector(mDropDownListHighlight); +            } +            mDropDownList.setAdapter(mAdapter); +            mDropDownList.setOnItemClickListener(mItemClickListener); +            mDropDownList.setFocusable(true); +            mDropDownList.setFocusableInTouchMode(true); +            mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { +                public void onItemSelected(AdapterView<?> parent, View view, +                        int position, long id) { + +                    if (position != -1) { +                        DropDownListView dropDownList = mDropDownList; + +                        if (dropDownList != null) { +                            dropDownList.mListSelectionHidden = false; +                        } +                    } +                } + +                public void onNothingSelected(AdapterView<?> parent) { +                } +            }); +            mDropDownList.setOnScrollListener(mScrollListener); + +            if (mItemSelectedListener != null) { +                mDropDownList.setOnItemSelectedListener(mItemSelectedListener); +            } + +            dropDownView = mDropDownList; + +            View hintView = mPromptView; +            if (hintView != null) { +                // if an hint has been specified, we accomodate more space for it and +                // add a text view in the drop down menu, at the bottom of the list +                LinearLayout hintContainer = new LinearLayout(context); +                hintContainer.setOrientation(LinearLayout.VERTICAL); + +                LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams( +                        ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f +                ); + +                switch (mPromptPosition) { +                case POSITION_PROMPT_BELOW: +                    hintContainer.addView(dropDownView, hintParams); +                    hintContainer.addView(hintView); +                    break; + +                case POSITION_PROMPT_ABOVE: +                    hintContainer.addView(hintView); +                    hintContainer.addView(dropDownView, hintParams); +                    break; + +                default: +                    break; +                } + +                // measure the hint's height to find how much more vertical space +                // we need to add to the drop down's height +                int widthSpec = MeasureSpec.makeMeasureSpec(mDropDownWidth, MeasureSpec.AT_MOST); +                int heightSpec = MeasureSpec.UNSPECIFIED; +                hintView.measure(widthSpec, heightSpec); + +                hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams(); +                otherHeights = hintView.getMeasuredHeight() + hintParams.topMargin +                        + hintParams.bottomMargin; + +                dropDownView = hintContainer; +            } + +            mPopup.setContentView(dropDownView); +        } else { +            dropDownView = (ViewGroup) mPopup.getContentView(); +            final View view = mPromptView; +            if (view != null) { +                LinearLayout.LayoutParams hintParams = +                        (LinearLayout.LayoutParams) view.getLayoutParams(); +                otherHeights = view.getMeasuredHeight() + hintParams.topMargin +                        + hintParams.bottomMargin; +            } +        } + +        // getMaxAvailableHeight() subtracts the padding, so we put it back +        // to get the available height for the whole window +        int padding = 0; +        Drawable background = mPopup.getBackground(); +        if (background != null) { +            background.getPadding(mTempRect); +            padding = mTempRect.top + mTempRect.bottom; + +            // If we don't have an explicit vertical offset, determine one from the window +            // background so that content will line up. +            if (!mDropDownVerticalOffsetSet) { +                mDropDownVerticalOffset = -mTempRect.top; +            } +        } + +        // Max height available on the screen for a popup. +        boolean ignoreBottomDecorations = +                mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED; +        final int maxHeight = /*mPopup.*/getMaxAvailableHeight( +                mDropDownAnchorView, mDropDownVerticalOffset, ignoreBottomDecorations); + +        if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) { +            return maxHeight + padding; +        } + +        final int listContent = /*mDropDownList.*/measureHeightOfChildren(MeasureSpec.UNSPECIFIED, +                0, -1/*ListView.NO_POSITION*/, maxHeight - otherHeights, -1); +        // add padding only if the list has items in it, that way we don't show +        // the popup if it is not needed +        if (listContent > 0) otherHeights += padding; + +        return listContent + otherHeights; +    } + +    private int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) { +        final Rect displayFrame = new Rect(); +        anchor.getWindowVisibleDisplayFrame(displayFrame); + +        final int[] anchorPos = new int[2]; +        anchor.getLocationOnScreen(anchorPos); + +        int bottomEdge = displayFrame.bottom; +        if (ignoreBottomDecorations) { +            Resources res = anchor.getContext().getResources(); +            bottomEdge = res.getDisplayMetrics().heightPixels; +        } +        final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset; +        final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset; + +        // anchorPos[1] is distance from anchor to top of screen +        int returnedHeight = Math.max(distanceToBottom, distanceToTop); +        if (mPopup.getBackground() != null) { +            mPopup.getBackground().getPadding(mTempRect); +            returnedHeight -= mTempRect.top + mTempRect.bottom; +        } + +        return returnedHeight; +    } + +    private int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition, +            final int maxHeight, int disallowPartialChildPosition) { + +        final ListAdapter adapter = mAdapter; +        if (adapter == null) { +            return mDropDownList.getListPaddingTop() + mDropDownList.getListPaddingBottom(); +        } + +        // Include the padding of the list +        int returnedHeight = mDropDownList.getListPaddingTop() + mDropDownList.getListPaddingBottom(); +        final int dividerHeight = ((mDropDownList.getDividerHeight() > 0) && mDropDownList.getDivider() != null) ? mDropDownList.getDividerHeight() : 0; +        // The previous height value that was less than maxHeight and contained +        // no partial children +        int prevHeightWithoutPartialChild = 0; +        int i; +        View child; + +        // mItemCount - 1 since endPosition parameter is inclusive +        endPosition = (endPosition == -1/*NO_POSITION*/) ? adapter.getCount() - 1 : endPosition; + +        for (i = startPosition; i <= endPosition; ++i) { +            child = mAdapter.getView(i, null, mDropDownList); +            if (mDropDownList.getCacheColorHint() != 0) { +                child.setDrawingCacheBackgroundColor(mDropDownList.getCacheColorHint()); +            } + +            measureScrapChild(child, i, widthMeasureSpec); + +            if (i > 0) { +                // Count the divider for all but one child +                returnedHeight += dividerHeight; +            } + +            returnedHeight += child.getMeasuredHeight(); + +            if (returnedHeight >= maxHeight) { +                // We went over, figure out which height to return.  If returnedHeight > maxHeight, +                // then the i'th position did not fit completely. +                return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1) +                            && (i > disallowPartialChildPosition) // We've past the min pos +                            && (prevHeightWithoutPartialChild > 0) // We have a prev height +                            && (returnedHeight != maxHeight) // i'th child did not fit completely +                        ? prevHeightWithoutPartialChild +                        : maxHeight; +            } + +            if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) { +                prevHeightWithoutPartialChild = returnedHeight; +            } +        } + +        // At this point, we went through the range of children, and they each +        // completely fit, so return the returnedHeight +        return returnedHeight; +    } +    private void measureScrapChild(View child, int position, int widthMeasureSpec) { +        ListView.LayoutParams p = (ListView.LayoutParams) child.getLayoutParams(); +        if (p == null) { +            p = new ListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, +                    ViewGroup.LayoutParams.WRAP_CONTENT, 0); +            child.setLayoutParams(p); +        } +        //XXX p.viewType = mAdapter.getItemViewType(position); +        //XXX p.forceAdd = true; + +        int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec, +                mDropDownList.getPaddingLeft() + mDropDownList.getPaddingRight(), p.width); +        int lpHeight = p.height; +        int childHeightSpec; +        if (lpHeight > 0) { +            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); +        } else { +            childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); +        } +        child.measure(childWidthSpec, childHeightSpec); +    } + +    private static class DropDownListView extends ListView { +        /* +         * WARNING: This is a workaround for a touch mode issue. +         * +         * Touch mode is propagated lazily to windows. This causes problems in +         * the following scenario: +         * - Type something in the AutoCompleteTextView and get some results +         * - Move down with the d-pad to select an item in the list +         * - Move up with the d-pad until the selection disappears +         * - Type more text in the AutoCompleteTextView *using the soft keyboard* +         *   and get new results; you are now in touch mode +         * - The selection comes back on the first item in the list, even though +         *   the list is supposed to be in touch mode +         * +         * Using the soft keyboard triggers the touch mode change but that change +         * is propagated to our window only after the first list layout, therefore +         * after the list attempts to resurrect the selection. +         * +         * The trick to work around this issue is to pretend the list is in touch +         * mode when we know that the selection should not appear, that is when +         * we know the user moved the selection away from the list. +         * +         * This boolean is set to true whenever we explicitly hide the list's +         * selection and reset to false whenever we know the user moved the +         * selection back to the list. +         * +         * When this boolean is true, isInTouchMode() returns true, otherwise it +         * returns super.isInTouchMode(). +         */ +        private boolean mListSelectionHidden; + +        private boolean mHijackFocus; + +        public DropDownListView(Context context, boolean hijackFocus) { +            super(context, null, /*com.android.internal.*/R.attr.dropDownListViewStyle); +            mHijackFocus = hijackFocus; +            // TODO: Add an API to control this +            setCacheColorHint(0); // Transparent, since the background drawable could be anything. +        } + +        //XXX @Override +        //View obtainView(int position, boolean[] isScrap) { +        //    View view = super.obtainView(position, isScrap); + +        //    if (view instanceof TextView) { +        //        ((TextView) view).setHorizontallyScrolling(true); +        //    } + +        //    return view; +        //} + +        @Override +        public boolean isInTouchMode() { +            // WARNING: Please read the comment where mListSelectionHidden is declared +            return (mHijackFocus && mListSelectionHidden) || super.isInTouchMode(); +        } + +        @Override +        public boolean hasWindowFocus() { +            return mHijackFocus || super.hasWindowFocus(); +        } + +        @Override +        public boolean isFocused() { +            return mHijackFocus || super.isFocused(); +        } + +        @Override +        public boolean hasFocus() { +            return mHijackFocus || super.hasFocus(); +        } +    } + +    private class PopupDataSetObserver extends DataSetObserver { +        @Override +        public void onChanged() { +            if (isShowing()) { +                // Resize the popup to fit new content +                show(); +            } +        } + +        @Override +        public void onInvalidated() { +            dismiss(); +        } +    } + +    private class ListSelectorHider implements Runnable { +        public void run() { +            clearListSelection(); +        } +    } + +    private class ResizePopupRunnable implements Runnable { +        public void run() { +            if (mDropDownList != null && mDropDownList.getCount() > mDropDownList.getChildCount() && +                    mDropDownList.getChildCount() <= mListItemExpandMaximum) { +                mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); +                show(); +            } +        } +    } + +    private class PopupTouchInterceptor implements OnTouchListener { +        public boolean onTouch(View v, MotionEvent event) { +            final int action = event.getAction(); +            final int x = (int) event.getX(); +            final int y = (int) event.getY(); + +            if (action == MotionEvent.ACTION_DOWN && +                    mPopup != null && mPopup.isShowing() && +                    (x >= 0 && x < mPopup.getWidth() && y >= 0 && y < mPopup.getHeight())) { +                mHandler.postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT); +            } else if (action == MotionEvent.ACTION_UP) { +                mHandler.removeCallbacks(mResizePopupRunnable); +            } +            return false; +        } +    } + +    private class PopupScrollListener implements ListView.OnScrollListener { +        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, +                int totalItemCount) { + +        } + +        public void onScrollStateChanged(AbsListView view, int scrollState) { +            if (scrollState == SCROLL_STATE_TOUCH_SCROLL && +                    !isInputMethodNotNeeded() && mPopup.getContentView() != null) { +                mHandler.removeCallbacks(mResizePopupRunnable); +                mResizePopupRunnable.run(); +            } +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsProgressBar.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsProgressBar.java new file mode 100644 index 000000000..1c02d4aca --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsProgressBar.java @@ -0,0 +1,1193 @@ +/* + * Copyright (C) 2006 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.actionbarsherlock.internal.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.AnimationDrawable; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ClipDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.RoundRectShape; +import android.graphics.drawable.shapes.Shape; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.view.ViewDebug; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; +import android.view.animation.Transformation; +import android.widget.RemoteViews.RemoteView; + + +/** + * <p> + * Visual indicator of progress in some operation.  Displays a bar to the user + * representing how far the operation has progressed; the application can + * change the amount of progress (modifying the length of the bar) as it moves + * forward.  There is also a secondary progress displayable on a progress bar + * which is useful for displaying intermediate progress, such as the buffer + * level during a streaming playback progress bar. + * </p> + * + * <p> + * A progress bar can also be made indeterminate. In indeterminate mode, the + * progress bar shows a cyclic animation without an indication of progress. This mode is used by + * applications when the length of the task is unknown. The indeterminate progress bar can be either + * a spinning wheel or a horizontal bar. + * </p> + * + * <p>The following code example shows how a progress bar can be used from + * a worker thread to update the user interface to notify the user of progress: + * </p> + * + * <pre> + * public class MyActivity extends Activity { + *     private static final int PROGRESS = 0x1; + * + *     private ProgressBar mProgress; + *     private int mProgressStatus = 0; + * + *     private Handler mHandler = new Handler(); + * + *     protected void onCreate(Bundle icicle) { + *         super.onCreate(icicle); + * + *         setContentView(R.layout.progressbar_activity); + * + *         mProgress = (ProgressBar) findViewById(R.id.progress_bar); + * + *         // Start lengthy operation in a background thread + *         new Thread(new Runnable() { + *             public void run() { + *                 while (mProgressStatus < 100) { + *                     mProgressStatus = doWork(); + * + *                     // Update the progress bar + *                     mHandler.post(new Runnable() { + *                         public void run() { + *                             mProgress.setProgress(mProgressStatus); + *                         } + *                     }); + *                 } + *             } + *         }).start(); + *     } + * }</pre> + * + * <p>To add a progress bar to a layout file, you can use the {@code <ProgressBar>} element. + * By default, the progress bar is a spinning wheel (an indeterminate indicator). To change to a + * horizontal progress bar, apply the {@link android.R.style#Widget_ProgressBar_Horizontal + * Widget.ProgressBar.Horizontal} style, like so:</p> + * + * <pre> + * <ProgressBar + *     style="@android:style/Widget.ProgressBar.Horizontal" + *     ... /></pre> + * + * <p>If you will use the progress bar to show real progress, you must use the horizontal bar. You + * can then increment the  progress with {@link #incrementProgressBy incrementProgressBy()} or + * {@link #setProgress setProgress()}. By default, the progress bar is full when it reaches 100. If + * necessary, you can adjust the maximum value (the value for a full bar) using the {@link + * android.R.styleable#ProgressBar_max android:max} attribute. Other attributes available are listed + * below.</p> + * + * <p>Another common style to apply to the progress bar is {@link + * android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}, which shows a smaller + * version of the spinning wheel—useful when waiting for content to load. + * For example, you can insert this kind of progress bar into your default layout for + * a view that will be populated by some content fetched from the Internet—the spinning wheel + * appears immediately and when your application receives the content, it replaces the progress bar + * with the loaded content. For example:</p> + * + * <pre> + * <LinearLayout + *     android:orientation="horizontal" + *     ... > + *     <ProgressBar + *         android:layout_width="wrap_content" + *         android:layout_height="wrap_content" + *         style="@android:style/Widget.ProgressBar.Small" + *         android:layout_marginRight="5dp" /> + *     <TextView + *         android:layout_width="wrap_content" + *         android:layout_height="wrap_content" + *         android:text="@string/loading" /> + * </LinearLayout></pre> + * + * <p>Other progress bar styles provided by the system include:</p> + * <ul> + * <li>{@link android.R.style#Widget_ProgressBar_Horizontal Widget.ProgressBar.Horizontal}</li> + * <li>{@link android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}</li> + * <li>{@link android.R.style#Widget_ProgressBar_Large Widget.ProgressBar.Large}</li> + * <li>{@link android.R.style#Widget_ProgressBar_Inverse Widget.ProgressBar.Inverse}</li> + * <li>{@link android.R.style#Widget_ProgressBar_Small_Inverse + * Widget.ProgressBar.Small.Inverse}</li> + * <li>{@link android.R.style#Widget_ProgressBar_Large_Inverse + * Widget.ProgressBar.Large.Inverse}</li> + * </ul> + * <p>The "inverse" styles provide an inverse color scheme for the spinner, which may be necessary + * if your application uses a light colored theme (a white background).</p> + * + * <p><strong>XML attributes</b></strong> + * <p> + * See {@link android.R.styleable#ProgressBar ProgressBar Attributes}, + * {@link android.R.styleable#View View Attributes} + * </p> + * + * @attr ref android.R.styleable#ProgressBar_animationResolution + * @attr ref android.R.styleable#ProgressBar_indeterminate + * @attr ref android.R.styleable#ProgressBar_indeterminateBehavior + * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable + * @attr ref android.R.styleable#ProgressBar_indeterminateDuration + * @attr ref android.R.styleable#ProgressBar_indeterminateOnly + * @attr ref android.R.styleable#ProgressBar_interpolator + * @attr ref android.R.styleable#ProgressBar_max + * @attr ref android.R.styleable#ProgressBar_maxHeight + * @attr ref android.R.styleable#ProgressBar_maxWidth + * @attr ref android.R.styleable#ProgressBar_minHeight + * @attr ref android.R.styleable#ProgressBar_minWidth + * @attr ref android.R.styleable#ProgressBar_progress + * @attr ref android.R.styleable#ProgressBar_progressDrawable + * @attr ref android.R.styleable#ProgressBar_secondaryProgress + */ +@RemoteView +public class IcsProgressBar extends View { +    private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; +    private static final int MAX_LEVEL = 10000; +    private static final int ANIMATION_RESOLUTION = 200; +    private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200; + +    private static final int[] ProgressBar = new int[] { +        android.R.attr.maxWidth, +        android.R.attr.maxHeight, +        android.R.attr.max, +        android.R.attr.progress, +        android.R.attr.secondaryProgress, +        android.R.attr.indeterminate, +        android.R.attr.indeterminateOnly, +        android.R.attr.indeterminateDrawable, +        android.R.attr.progressDrawable, +        android.R.attr.indeterminateDuration, +        android.R.attr.indeterminateBehavior, +        android.R.attr.minWidth, +        android.R.attr.minHeight, +        android.R.attr.interpolator, +        android.R.attr.animationResolution, +    }; +    private static final int ProgressBar_maxWidth = 0; +    private static final int ProgressBar_maxHeight = 1; +    private static final int ProgressBar_max = 2; +    private static final int ProgressBar_progress = 3; +    private static final int ProgressBar_secondaryProgress = 4; +    private static final int ProgressBar_indeterminate = 5; +    private static final int ProgressBar_indeterminateOnly = 6; +    private static final int ProgressBar_indeterminateDrawable = 7; +    private static final int ProgressBar_progressDrawable = 8; +    private static final int ProgressBar_indeterminateDuration = 9; +    private static final int ProgressBar_indeterminateBehavior = 10; +    private static final int ProgressBar_minWidth = 11; +    private static final int ProgressBar_minHeight = 12; +    private static final int ProgressBar_interpolator = 13; +    private static final int ProgressBar_animationResolution = 14; + +    int mMinWidth; +    int mMaxWidth; +    int mMinHeight; +    int mMaxHeight; + +    private int mProgress; +    private int mSecondaryProgress; +    private int mMax; + +    private int mBehavior; +    private int mDuration; +    private boolean mIndeterminate; +    private boolean mOnlyIndeterminate; +    private Transformation mTransformation; +    private AlphaAnimation mAnimation; +    private Drawable mIndeterminateDrawable; +    private int mIndeterminateRealLeft; +    private int mIndeterminateRealTop; +    private Drawable mProgressDrawable; +    private Drawable mCurrentDrawable; +    Bitmap mSampleTile; +    private boolean mNoInvalidate; +    private Interpolator mInterpolator; +    private RefreshProgressRunnable mRefreshProgressRunnable; +    private long mUiThreadId; +    private boolean mShouldStartAnimationDrawable; +    private long mLastDrawTime; + +    private boolean mInDrawing; + +    private int mAnimationResolution; + +    private AccessibilityManager mAccessibilityManager; +    private AccessibilityEventSender mAccessibilityEventSender; + +    /** +     * Create a new progress bar with range 0...100 and initial progress of 0. +     * @param context the application environment +     */ +    public IcsProgressBar(Context context) { +        this(context, null); +    } + +    public IcsProgressBar(Context context, AttributeSet attrs) { +        this(context, attrs, android.R.attr.progressBarStyle); +    } + +    public IcsProgressBar(Context context, AttributeSet attrs, int defStyle) { +        this(context, attrs, defStyle, 0); +    } + +    /** +     * @hide +     */ +    public IcsProgressBar(Context context, AttributeSet attrs, int defStyle, int styleRes) { +        super(context, attrs, defStyle); +        mUiThreadId = Thread.currentThread().getId(); +        initProgressBar(); + +        TypedArray a = +            context.obtainStyledAttributes(attrs, /*R.styleable.*/ProgressBar, defStyle, styleRes); + +        mNoInvalidate = true; + +        Drawable drawable = a.getDrawable(/*R.styleable.*/ProgressBar_progressDrawable); +        if (drawable != null) { +            drawable = tileify(drawable, false); +            // Calling this method can set mMaxHeight, make sure the corresponding +            // XML attribute for mMaxHeight is read after calling this method +            setProgressDrawable(drawable); +        } + + +        mDuration = a.getInt(/*R.styleable.*/ProgressBar_indeterminateDuration, mDuration); + +        mMinWidth = a.getDimensionPixelSize(/*R.styleable.*/ProgressBar_minWidth, mMinWidth); +        mMaxWidth = a.getDimensionPixelSize(/*R.styleable.*/ProgressBar_maxWidth, mMaxWidth); +        mMinHeight = a.getDimensionPixelSize(/*R.styleable.*/ProgressBar_minHeight, mMinHeight); +        mMaxHeight = a.getDimensionPixelSize(/*R.styleable.*/ProgressBar_maxHeight, mMaxHeight); + +        mBehavior = a.getInt(/*R.styleable.*/ProgressBar_indeterminateBehavior, mBehavior); + +        final int resID = a.getResourceId( +                /*com.android.internal.R.styleable.*/ProgressBar_interpolator, +                android.R.anim.linear_interpolator); // default to linear interpolator +        if (resID > 0) { +            setInterpolator(context, resID); +        } + +        setMax(a.getInt(/*R.styleable.*/ProgressBar_max, mMax)); + +        setProgress(a.getInt(/*R.styleable.*/ProgressBar_progress, mProgress)); + +        setSecondaryProgress( +                a.getInt(/*R.styleable.*/ProgressBar_secondaryProgress, mSecondaryProgress)); + +        drawable = a.getDrawable(/*R.styleable.*/ProgressBar_indeterminateDrawable); +        if (drawable != null) { +            drawable = tileifyIndeterminate(drawable); +            setIndeterminateDrawable(drawable); +        } + +        mOnlyIndeterminate = a.getBoolean( +                /*R.styleable.*/ProgressBar_indeterminateOnly, mOnlyIndeterminate); + +        mNoInvalidate = false; + +        setIndeterminate(mOnlyIndeterminate || a.getBoolean( +                /*R.styleable.*/ProgressBar_indeterminate, mIndeterminate)); + +        mAnimationResolution = a.getInteger(/*R.styleable.*/ProgressBar_animationResolution, +                ANIMATION_RESOLUTION); + +        a.recycle(); + +        mAccessibilityManager = (AccessibilityManager)context.getSystemService(Context.ACCESSIBILITY_SERVICE); +    } + +    /** +     * Converts a drawable to a tiled version of itself. It will recursively +     * traverse layer and state list drawables. +     */ +    private Drawable tileify(Drawable drawable, boolean clip) { + +        if (drawable instanceof LayerDrawable) { +            LayerDrawable background = (LayerDrawable) drawable; +            final int N = background.getNumberOfLayers(); +            Drawable[] outDrawables = new Drawable[N]; + +            for (int i = 0; i < N; i++) { +                int id = background.getId(i); +                outDrawables[i] = tileify(background.getDrawable(i), +                        (id == android.R.id.progress || id == android.R.id.secondaryProgress)); +            } + +            LayerDrawable newBg = new LayerDrawable(outDrawables); + +            for (int i = 0; i < N; i++) { +                newBg.setId(i, background.getId(i)); +            } + +            return newBg; + +        }/* else if (drawable instanceof StateListDrawable) { +            StateListDrawable in = (StateListDrawable) drawable; +            StateListDrawable out = new StateListDrawable(); +            int numStates = in.getStateCount(); +            for (int i = 0; i < numStates; i++) { +                out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip)); +            } +            return out; + +        }*/ else if (drawable instanceof BitmapDrawable) { +            final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap(); +            if (mSampleTile == null) { +                mSampleTile = tileBitmap; +            } + +            final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape()); + +            final BitmapShader bitmapShader = new BitmapShader(tileBitmap, +                    Shader.TileMode.REPEAT, Shader.TileMode.CLAMP); +            shapeDrawable.getPaint().setShader(bitmapShader); + +            return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT, +                    ClipDrawable.HORIZONTAL) : shapeDrawable; +        } + +        return drawable; +    } + +    Shape getDrawableShape() { +        final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 }; +        return new RoundRectShape(roundedCorners, null, null); +    } + +    /** +     * Convert a AnimationDrawable for use as a barberpole animation. +     * Each frame of the animation is wrapped in a ClipDrawable and +     * given a tiling BitmapShader. +     */ +    private Drawable tileifyIndeterminate(Drawable drawable) { +        if (drawable instanceof AnimationDrawable) { +            AnimationDrawable background = (AnimationDrawable) drawable; +            final int N = background.getNumberOfFrames(); +            AnimationDrawable newBg = new AnimationDrawable(); +            newBg.setOneShot(background.isOneShot()); + +            for (int i = 0; i < N; i++) { +                Drawable frame = tileify(background.getFrame(i), true); +                frame.setLevel(10000); +                newBg.addFrame(frame, background.getDuration(i)); +            } +            newBg.setLevel(10000); +            drawable = newBg; +        } +        return drawable; +    } + +    /** +     * <p> +     * Initialize the progress bar's default values: +     * </p> +     * <ul> +     * <li>progress = 0</li> +     * <li>max = 100</li> +     * <li>animation duration = 4000 ms</li> +     * <li>indeterminate = false</li> +     * <li>behavior = repeat</li> +     * </ul> +     */ +    private void initProgressBar() { +        mMax = 100; +        mProgress = 0; +        mSecondaryProgress = 0; +        mIndeterminate = false; +        mOnlyIndeterminate = false; +        mDuration = 4000; +        mBehavior = AlphaAnimation.RESTART; +        mMinWidth = 24; +        mMaxWidth = 48; +        mMinHeight = 24; +        mMaxHeight = 48; +    } + +    /** +     * <p>Indicate whether this progress bar is in indeterminate mode.</p> +     * +     * @return true if the progress bar is in indeterminate mode +     */ +    @ViewDebug.ExportedProperty(category = "progress") +    public synchronized boolean isIndeterminate() { +        return mIndeterminate; +    } + +    /** +     * <p>Change the indeterminate mode for this progress bar. In indeterminate +     * mode, the progress is ignored and the progress bar shows an infinite +     * animation instead.</p> +     * +     * If this progress bar's style only supports indeterminate mode (such as the circular +     * progress bars), then this will be ignored. +     * +     * @param indeterminate true to enable the indeterminate mode +     */ +    public synchronized void setIndeterminate(boolean indeterminate) { +        if ((!mOnlyIndeterminate || !mIndeterminate) && indeterminate != mIndeterminate) { +            mIndeterminate = indeterminate; + +            if (indeterminate) { +                // swap between indeterminate and regular backgrounds +                mCurrentDrawable = mIndeterminateDrawable; +                startAnimation(); +            } else { +                mCurrentDrawable = mProgressDrawable; +                stopAnimation(); +            } +        } +    } + +    /** +     * <p>Get the drawable used to draw the progress bar in +     * indeterminate mode.</p> +     * +     * @return a {@link android.graphics.drawable.Drawable} instance +     * +     * @see #setIndeterminateDrawable(android.graphics.drawable.Drawable) +     * @see #setIndeterminate(boolean) +     */ +    public Drawable getIndeterminateDrawable() { +        return mIndeterminateDrawable; +    } + +    /** +     * <p>Define the drawable used to draw the progress bar in +     * indeterminate mode.</p> +     * +     * @param d the new drawable +     * +     * @see #getIndeterminateDrawable() +     * @see #setIndeterminate(boolean) +     */ +    public void setIndeterminateDrawable(Drawable d) { +        if (d != null) { +            d.setCallback(this); +        } +        mIndeterminateDrawable = d; +        if (mIndeterminate) { +            mCurrentDrawable = d; +            postInvalidate(); +        } +    } + +    /** +     * <p>Get the drawable used to draw the progress bar in +     * progress mode.</p> +     * +     * @return a {@link android.graphics.drawable.Drawable} instance +     * +     * @see #setProgressDrawable(android.graphics.drawable.Drawable) +     * @see #setIndeterminate(boolean) +     */ +    public Drawable getProgressDrawable() { +        return mProgressDrawable; +    } + +    /** +     * <p>Define the drawable used to draw the progress bar in +     * progress mode.</p> +     * +     * @param d the new drawable +     * +     * @see #getProgressDrawable() +     * @see #setIndeterminate(boolean) +     */ +    public void setProgressDrawable(Drawable d) { +        boolean needUpdate; +        if (mProgressDrawable != null && d != mProgressDrawable) { +            mProgressDrawable.setCallback(null); +            needUpdate = true; +        } else { +            needUpdate = false; +        } + +        if (d != null) { +            d.setCallback(this); + +            // Make sure the ProgressBar is always tall enough +            int drawableHeight = d.getMinimumHeight(); +            if (mMaxHeight < drawableHeight) { +                mMaxHeight = drawableHeight; +                requestLayout(); +            } +        } +        mProgressDrawable = d; +        if (!mIndeterminate) { +            mCurrentDrawable = d; +            postInvalidate(); +        } + +        if (needUpdate) { +            updateDrawableBounds(getWidth(), getHeight()); +            updateDrawableState(); +            doRefreshProgress(android.R.id.progress, mProgress, false, false); +            doRefreshProgress(android.R.id.secondaryProgress, mSecondaryProgress, false, false); +        } +    } + +    /** +     * @return The drawable currently used to draw the progress bar +     */ +    Drawable getCurrentDrawable() { +        return mCurrentDrawable; +    } + +    @Override +    protected boolean verifyDrawable(Drawable who) { +        return who == mProgressDrawable || who == mIndeterminateDrawable +                || super.verifyDrawable(who); +    } + +    @Override +    public void jumpDrawablesToCurrentState() { +        super.jumpDrawablesToCurrentState(); +        if (mProgressDrawable != null) mProgressDrawable.jumpToCurrentState(); +        if (mIndeterminateDrawable != null) mIndeterminateDrawable.jumpToCurrentState(); +    } + +    @Override +    public void postInvalidate() { +        if (!mNoInvalidate) { +            super.postInvalidate(); +        } +    } + +    private class RefreshProgressRunnable implements Runnable { + +        private int mId; +        private int mProgress; +        private boolean mFromUser; + +        RefreshProgressRunnable(int id, int progress, boolean fromUser) { +            mId = id; +            mProgress = progress; +            mFromUser = fromUser; +        } + +        public void run() { +            doRefreshProgress(mId, mProgress, mFromUser, true); +            // Put ourselves back in the cache when we are done +            mRefreshProgressRunnable = this; +        } + +        public void setup(int id, int progress, boolean fromUser) { +            mId = id; +            mProgress = progress; +            mFromUser = fromUser; +        } + +    } + +    private synchronized void doRefreshProgress(int id, int progress, boolean fromUser, +            boolean callBackToApp) { +        float scale = mMax > 0 ? (float) progress / (float) mMax : 0; +        final Drawable d = mCurrentDrawable; +        if (d != null) { +            Drawable progressDrawable = null; + +            if (d instanceof LayerDrawable) { +                progressDrawable = ((LayerDrawable) d).findDrawableByLayerId(id); +            } + +            final int level = (int) (scale * MAX_LEVEL); +            (progressDrawable != null ? progressDrawable : d).setLevel(level); +        } else { +            invalidate(); +        } + +        if (callBackToApp && id == android.R.id.progress) { +            onProgressRefresh(scale, fromUser); +        } +    } + +    void onProgressRefresh(float scale, boolean fromUser) { +        if (mAccessibilityManager.isEnabled()) { +            scheduleAccessibilityEventSender(); +        } +    } + +    private synchronized void refreshProgress(int id, int progress, boolean fromUser) { +        if (mUiThreadId == Thread.currentThread().getId()) { +            doRefreshProgress(id, progress, fromUser, true); +        } else { +            RefreshProgressRunnable r; +            if (mRefreshProgressRunnable != null) { +                // Use cached RefreshProgressRunnable if available +                r = mRefreshProgressRunnable; +                // Uncache it +                mRefreshProgressRunnable = null; +                r.setup(id, progress, fromUser); +            } else { +                // Make a new one +                r = new RefreshProgressRunnable(id, progress, fromUser); +            } +            post(r); +        } +    } + +    /** +     * <p>Set the current progress to the specified value. Does not do anything +     * if the progress bar is in indeterminate mode.</p> +     * +     * @param progress the new progress, between 0 and {@link #getMax()} +     * +     * @see #setIndeterminate(boolean) +     * @see #isIndeterminate() +     * @see #getProgress() +     * @see #incrementProgressBy(int) +     */ +    public synchronized void setProgress(int progress) { +        setProgress(progress, false); +    } + +    synchronized void setProgress(int progress, boolean fromUser) { +        if (mIndeterminate) { +            return; +        } + +        if (progress < 0) { +            progress = 0; +        } + +        if (progress > mMax) { +            progress = mMax; +        } + +        if (progress != mProgress) { +            mProgress = progress; +            refreshProgress(android.R.id.progress, mProgress, fromUser); +        } +    } + +    /** +     * <p> +     * Set the current secondary progress to the specified value. Does not do +     * anything if the progress bar is in indeterminate mode. +     * </p> +     * +     * @param secondaryProgress the new secondary progress, between 0 and {@link #getMax()} +     * @see #setIndeterminate(boolean) +     * @see #isIndeterminate() +     * @see #getSecondaryProgress() +     * @see #incrementSecondaryProgressBy(int) +     */ +    public synchronized void setSecondaryProgress(int secondaryProgress) { +        if (mIndeterminate) { +            return; +        } + +        if (secondaryProgress < 0) { +            secondaryProgress = 0; +        } + +        if (secondaryProgress > mMax) { +            secondaryProgress = mMax; +        } + +        if (secondaryProgress != mSecondaryProgress) { +            mSecondaryProgress = secondaryProgress; +            refreshProgress(android.R.id.secondaryProgress, mSecondaryProgress, false); +        } +    } + +    /** +     * <p>Get the progress bar's current level of progress. Return 0 when the +     * progress bar is in indeterminate mode.</p> +     * +     * @return the current progress, between 0 and {@link #getMax()} +     * +     * @see #setIndeterminate(boolean) +     * @see #isIndeterminate() +     * @see #setProgress(int) +     * @see #setMax(int) +     * @see #getMax() +     */ +    @ViewDebug.ExportedProperty(category = "progress") +    public synchronized int getProgress() { +        return mIndeterminate ? 0 : mProgress; +    } + +    /** +     * <p>Get the progress bar's current level of secondary progress. Return 0 when the +     * progress bar is in indeterminate mode.</p> +     * +     * @return the current secondary progress, between 0 and {@link #getMax()} +     * +     * @see #setIndeterminate(boolean) +     * @see #isIndeterminate() +     * @see #setSecondaryProgress(int) +     * @see #setMax(int) +     * @see #getMax() +     */ +    @ViewDebug.ExportedProperty(category = "progress") +    public synchronized int getSecondaryProgress() { +        return mIndeterminate ? 0 : mSecondaryProgress; +    } + +    /** +     * <p>Return the upper limit of this progress bar's range.</p> +     * +     * @return a positive integer +     * +     * @see #setMax(int) +     * @see #getProgress() +     * @see #getSecondaryProgress() +     */ +    @ViewDebug.ExportedProperty(category = "progress") +    public synchronized int getMax() { +        return mMax; +    } + +    /** +     * <p>Set the range of the progress bar to 0...<tt>max</tt>.</p> +     * +     * @param max the upper range of this progress bar +     * +     * @see #getMax() +     * @see #setProgress(int) +     * @see #setSecondaryProgress(int) +     */ +    public synchronized void setMax(int max) { +        if (max < 0) { +            max = 0; +        } +        if (max != mMax) { +            mMax = max; +            postInvalidate(); + +            if (mProgress > max) { +                mProgress = max; +            } +            refreshProgress(android.R.id.progress, mProgress, false); +        } +    } + +    /** +     * <p>Increase the progress bar's progress by the specified amount.</p> +     * +     * @param diff the amount by which the progress must be increased +     * +     * @see #setProgress(int) +     */ +    public synchronized final void incrementProgressBy(int diff) { +        setProgress(mProgress + diff); +    } + +    /** +     * <p>Increase the progress bar's secondary progress by the specified amount.</p> +     * +     * @param diff the amount by which the secondary progress must be increased +     * +     * @see #setSecondaryProgress(int) +     */ +    public synchronized final void incrementSecondaryProgressBy(int diff) { +        setSecondaryProgress(mSecondaryProgress + diff); +    } + +    /** +     * <p>Start the indeterminate progress animation.</p> +     */ +    void startAnimation() { +        if (getVisibility() != VISIBLE) { +            return; +        } + +        if (mIndeterminateDrawable instanceof Animatable) { +            mShouldStartAnimationDrawable = true; +            mAnimation = null; +        } else { +            if (mInterpolator == null) { +                mInterpolator = new LinearInterpolator(); +            } + +            mTransformation = new Transformation(); +            mAnimation = new AlphaAnimation(0.0f, 1.0f); +            mAnimation.setRepeatMode(mBehavior); +            mAnimation.setRepeatCount(Animation.INFINITE); +            mAnimation.setDuration(mDuration); +            mAnimation.setInterpolator(mInterpolator); +            mAnimation.setStartTime(Animation.START_ON_FIRST_FRAME); +        } +        postInvalidate(); +    } + +    /** +     * <p>Stop the indeterminate progress animation.</p> +     */ +    void stopAnimation() { +        mAnimation = null; +        mTransformation = null; +        if (mIndeterminateDrawable instanceof Animatable) { +            ((Animatable) mIndeterminateDrawable).stop(); +            mShouldStartAnimationDrawable = false; +        } +        postInvalidate(); +    } + +    /** +     * Sets the acceleration curve for the indeterminate animation. +     * The interpolator is loaded as a resource from the specified context. +     * +     * @param context The application environment +     * @param resID The resource identifier of the interpolator to load +     */ +    public void setInterpolator(Context context, int resID) { +        setInterpolator(AnimationUtils.loadInterpolator(context, resID)); +    } + +    /** +     * Sets the acceleration curve for the indeterminate animation. +     * Defaults to a linear interpolation. +     * +     * @param interpolator The interpolator which defines the acceleration curve +     */ +    public void setInterpolator(Interpolator interpolator) { +        mInterpolator = interpolator; +    } + +    /** +     * Gets the acceleration curve type for the indeterminate animation. +     * +     * @return the {@link Interpolator} associated to this animation +     */ +    public Interpolator getInterpolator() { +        return mInterpolator; +    } + +    @Override +    public void setVisibility(int v) { +        if (getVisibility() != v) { +            super.setVisibility(v); + +            if (mIndeterminate) { +                // let's be nice with the UI thread +                if (v == GONE || v == INVISIBLE) { +                    stopAnimation(); +                } else { +                    startAnimation(); +                } +            } +        } +    } + +    @Override +    protected void onVisibilityChanged(View changedView, int visibility) { +        super.onVisibilityChanged(changedView, visibility); + +        if (mIndeterminate) { +            // let's be nice with the UI thread +            if (visibility == GONE || visibility == INVISIBLE) { +                stopAnimation(); +            } else { +                startAnimation(); +            } +        } +    } + +    @Override +    public void invalidateDrawable(Drawable dr) { +        if (!mInDrawing) { +            if (verifyDrawable(dr)) { +                final Rect dirty = dr.getBounds(); +                final int scrollX = getScrollX() + getPaddingLeft(); +                final int scrollY = getScrollY() + getPaddingTop(); + +                invalidate(dirty.left + scrollX, dirty.top + scrollY, +                        dirty.right + scrollX, dirty.bottom + scrollY); +            } else { +                super.invalidateDrawable(dr); +            } +        } +    } + +    /** +     * @hide +     * +    @Override +    public int getResolvedLayoutDirection(Drawable who) { +        return (who == mProgressDrawable || who == mIndeterminateDrawable) ? +            getResolvedLayoutDirection() : super.getResolvedLayoutDirection(who); +    } +    */ + +    @Override +    protected void onSizeChanged(int w, int h, int oldw, int oldh) { +        updateDrawableBounds(w, h); +    } + +    private void updateDrawableBounds(int w, int h) { +        // onDraw will translate the canvas so we draw starting at 0,0 +        int right = w - getPaddingRight() - getPaddingLeft(); +        int bottom = h - getPaddingBottom() - getPaddingTop(); +        int top = 0; +        int left = 0; + +        if (mIndeterminateDrawable != null) { +            // Aspect ratio logic does not apply to AnimationDrawables +            if (mOnlyIndeterminate && !(mIndeterminateDrawable instanceof AnimationDrawable)) { +                // Maintain aspect ratio. Certain kinds of animated drawables +                // get very confused otherwise. +                final int intrinsicWidth = mIndeterminateDrawable.getIntrinsicWidth(); +                final int intrinsicHeight = mIndeterminateDrawable.getIntrinsicHeight(); +                final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight; +                final float boundAspect = (float) w / h; +                if (intrinsicAspect != boundAspect) { +                    if (boundAspect > intrinsicAspect) { +                        // New width is larger. Make it smaller to match height. +                        final int width = (int) (h * intrinsicAspect); +                        left = (w - width) / 2; +                        right = left + width; +                    } else { +                        // New height is larger. Make it smaller to match width. +                        final int height = (int) (w * (1 / intrinsicAspect)); +                        top = (h - height) / 2; +                        bottom = top + height; +                    } +                } +            } +            mIndeterminateDrawable.setBounds(0, 0, right - left, bottom - top); +            mIndeterminateRealLeft = left; +            mIndeterminateRealTop = top; +        } + +        if (mProgressDrawable != null) { +            mProgressDrawable.setBounds(0, 0, right, bottom); +        } +    } + +    @Override +    protected synchronized void onDraw(Canvas canvas) { +        super.onDraw(canvas); + +        Drawable d = mCurrentDrawable; +        if (d != null) { +            // Translate canvas so a indeterminate circular progress bar with padding +            // rotates properly in its animation +            canvas.save(); +            canvas.translate(getPaddingLeft() + mIndeterminateRealLeft, getPaddingTop() + mIndeterminateRealTop); +            long time = getDrawingTime(); +            if (mAnimation != null) { +                mAnimation.getTransformation(time, mTransformation); +                float scale = mTransformation.getAlpha(); +                try { +                    mInDrawing = true; +                    d.setLevel((int) (scale * MAX_LEVEL)); +                } finally { +                    mInDrawing = false; +                } +                if (SystemClock.uptimeMillis() - mLastDrawTime >= mAnimationResolution) { +                    mLastDrawTime = SystemClock.uptimeMillis(); +                    postInvalidateDelayed(mAnimationResolution); +                } +            } +            d.draw(canvas); +            canvas.restore(); +            if (mShouldStartAnimationDrawable && d instanceof Animatable) { +                ((Animatable) d).start(); +                mShouldStartAnimationDrawable = false; +            } +        } +    } + +    @Override +    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        Drawable d = mCurrentDrawable; + +        int dw = 0; +        int dh = 0; +        if (d != null) { +            dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth())); +            dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight())); +        } +        updateDrawableState(); +        dw += getPaddingLeft() + getPaddingRight(); +        dh += getPaddingTop() + getPaddingBottom(); + +        if (IS_HONEYCOMB) { +            setMeasuredDimension(View.resolveSizeAndState(dw, widthMeasureSpec, 0), +                    View.resolveSizeAndState(dh, heightMeasureSpec, 0)); +        } else { +            setMeasuredDimension(View.resolveSize(dw, widthMeasureSpec), +                    View.resolveSize(dh, heightMeasureSpec)); +        } +    } + +    @Override +    protected void drawableStateChanged() { +        super.drawableStateChanged(); +        updateDrawableState(); +    } + +    private void updateDrawableState() { +        int[] state = getDrawableState(); + +        if (mProgressDrawable != null && mProgressDrawable.isStateful()) { +            mProgressDrawable.setState(state); +        } + +        if (mIndeterminateDrawable != null && mIndeterminateDrawable.isStateful()) { +            mIndeterminateDrawable.setState(state); +        } +    } + +    static class SavedState extends BaseSavedState { +        int progress; +        int secondaryProgress; + +        /** +         * Constructor called from {@link IcsProgressBar#onSaveInstanceState()} +         */ +        SavedState(Parcelable superState) { +            super(superState); +        } + +        /** +         * Constructor called from {@link #CREATOR} +         */ +        private SavedState(Parcel in) { +            super(in); +            progress = in.readInt(); +            secondaryProgress = in.readInt(); +        } + +        @Override +        public void writeToParcel(Parcel out, int flags) { +            super.writeToParcel(out, flags); +            out.writeInt(progress); +            out.writeInt(secondaryProgress); +        } + +        public static final Parcelable.Creator<SavedState> CREATOR +                = new Parcelable.Creator<SavedState>() { +            public SavedState createFromParcel(Parcel in) { +                return new SavedState(in); +            } + +            public SavedState[] newArray(int size) { +                return new SavedState[size]; +            } +        }; +    } + +    @Override +    public Parcelable onSaveInstanceState() { +        // Force our ancestor class to save its state +        Parcelable superState = super.onSaveInstanceState(); +        SavedState ss = new SavedState(superState); + +        ss.progress = mProgress; +        ss.secondaryProgress = mSecondaryProgress; + +        return ss; +    } + +    @Override +    public void onRestoreInstanceState(Parcelable state) { +        SavedState ss = (SavedState) state; +        super.onRestoreInstanceState(ss.getSuperState()); + +        setProgress(ss.progress); +        setSecondaryProgress(ss.secondaryProgress); +    } + +    @Override +    protected void onAttachedToWindow() { +        super.onAttachedToWindow(); +        if (mIndeterminate) { +            startAnimation(); +        } +    } + +    @Override +    protected void onDetachedFromWindow() { +        if (mIndeterminate) { +            stopAnimation(); +        } +        if(mRefreshProgressRunnable != null) { +            removeCallbacks(mRefreshProgressRunnable); +        } +        if (mAccessibilityEventSender != null) { +            removeCallbacks(mAccessibilityEventSender); +        } +        // This should come after stopAnimation(), otherwise an invalidate message remains in the +        // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation +        super.onDetachedFromWindow(); +    } + +    @Override +    public void onInitializeAccessibilityEvent(AccessibilityEvent event) { +        super.onInitializeAccessibilityEvent(event); +        event.setItemCount(mMax); +        event.setCurrentItemIndex(mProgress); +    } + +    /** +     * Schedule a command for sending an accessibility event. +     * </br> +     * Note: A command is used to ensure that accessibility events +     *       are sent at most one in a given time frame to save +     *       system resources while the progress changes quickly. +     */ +    private void scheduleAccessibilityEventSender() { +        if (mAccessibilityEventSender == null) { +            mAccessibilityEventSender = new AccessibilityEventSender(); +        } else { +            removeCallbacks(mAccessibilityEventSender); +        } +        postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT); +    } + +    /** +     * Command for sending an accessibility event. +     */ +    private class AccessibilityEventSender implements Runnable { +        public void run() { +            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsSpinner.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsSpinner.java new file mode 100644 index 000000000..038d1e031 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsSpinner.java @@ -0,0 +1,703 @@ +/* + * Copyright (C) 2007 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.actionbarsherlock.internal.widget; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import com.actionbarsherlock.R; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.res.TypedArray; +import android.database.DataSetObserver; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.PopupWindow; +import android.widget.SpinnerAdapter; + + +/** + * A view that displays one child at a time and lets the user pick among them. + * The items in the Spinner come from the {@link Adapter} associated with + * this view. + * + * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-spinner.html">Spinner + * tutorial</a>.</p> + * + * @attr ref android.R.styleable#Spinner_prompt + */ +public class IcsSpinner extends IcsAbsSpinner implements OnClickListener { +    //private static final String TAG = "Spinner"; + +    // Only measure this many items to get a decent max width. +    private static final int MAX_ITEMS_MEASURED = 15; + +    /** +     * Use a dialog window for selecting spinner options. +     */ +    //public static final int MODE_DIALOG = 0; + +    /** +     * Use a dropdown anchored to the Spinner for selecting spinner options. +     */ +    public static final int MODE_DROPDOWN = 1; + +    /** +     * Use the theme-supplied value to select the dropdown mode. +     */ +    //private static final int MODE_THEME = -1; + +    private SpinnerPopup mPopup; +    private DropDownAdapter mTempAdapter; +    int mDropDownWidth; + +    private int mGravity; +    private boolean mDisableChildrenWhenDisabled; + +    private Rect mTempRect = new Rect(); + +    public IcsSpinner(Context context, AttributeSet attrs) { +        this(context, attrs, R.attr.actionDropDownStyle); +    } + +    /** +     * Construct a new spinner with the given context's theme, the supplied attribute set, +     * and default style. +     * +     * @param context The Context the view is running in, through which it can +     *        access the current theme, resources, etc. +     * @param attrs The attributes of the XML tag that is inflating the view. +     * @param defStyle The default style to apply to this view. If 0, no style +     *        will be applied (beyond what is included in the theme). This may +     *        either be an attribute resource, whose value will be retrieved +     *        from the current theme, or an explicit style resource. +     */ +    public IcsSpinner(Context context, AttributeSet attrs, int defStyle) { +        super(context, attrs, defStyle); + +        TypedArray a = context.obtainStyledAttributes(attrs, +                R.styleable.SherlockSpinner, defStyle, 0); + + +        DropdownPopup popup = new DropdownPopup(context, attrs, defStyle); + +        mDropDownWidth = a.getLayoutDimension( +                R.styleable.SherlockSpinner_android_dropDownWidth, +                ViewGroup.LayoutParams.WRAP_CONTENT); +        popup.setBackgroundDrawable(a.getDrawable( +                R.styleable.SherlockSpinner_android_popupBackground)); +        final int verticalOffset = a.getDimensionPixelOffset( +                R.styleable.SherlockSpinner_android_dropDownVerticalOffset, 0); +        if (verticalOffset != 0) { +            popup.setVerticalOffset(verticalOffset); +        } + +        final int horizontalOffset = a.getDimensionPixelOffset( +                R.styleable.SherlockSpinner_android_dropDownHorizontalOffset, 0); +        if (horizontalOffset != 0) { +            popup.setHorizontalOffset(horizontalOffset); +        } + +        mPopup = popup; + +        mGravity = a.getInt(R.styleable.SherlockSpinner_android_gravity, Gravity.CENTER); + +        mPopup.setPromptText(a.getString(R.styleable.SherlockSpinner_android_prompt)); + +        mDisableChildrenWhenDisabled = true; + +        a.recycle(); + +        // Base constructor can call setAdapter before we initialize mPopup. +        // Finish setting things up if this happened. +        if (mTempAdapter != null) { +            mPopup.setAdapter(mTempAdapter); +            mTempAdapter = null; +        } +    } + +    @Override +    public void setEnabled(boolean enabled) { +        super.setEnabled(enabled); +        if (mDisableChildrenWhenDisabled) { +            final int count = getChildCount(); +            for (int i = 0; i < count; i++) { +                getChildAt(i).setEnabled(enabled); +            } +        } +    } + +    /** +     * Describes how the selected item view is positioned. Currently only the horizontal component +     * is used. The default is determined by the current theme. +     * +     * @param gravity See {@link android.view.Gravity} +     * +     * @attr ref android.R.styleable#Spinner_gravity +     */ +    public void setGravity(int gravity) { +        if (mGravity != gravity) { +            if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) { +                gravity |= Gravity.LEFT; +            } +            mGravity = gravity; +            requestLayout(); +        } +    } + +    @Override +    public void setAdapter(SpinnerAdapter adapter) { +        super.setAdapter(adapter); + +        if (mPopup != null) { +            mPopup.setAdapter(new DropDownAdapter(adapter)); +        } else { +            mTempAdapter = new DropDownAdapter(adapter); +        } +    } + +    @Override +    public int getBaseline() { +        View child = null; + +        if (getChildCount() > 0) { +            child = getChildAt(0); +        } else if (mAdapter != null && mAdapter.getCount() > 0) { +            child = makeAndAddView(0); +            mRecycler.put(0, child); +            removeAllViewsInLayout(); +        } + +        if (child != null) { +            final int childBaseline = child.getBaseline(); +            return childBaseline >= 0 ? child.getTop() + childBaseline : -1; +        } else { +            return -1; +        } +    } + +    @Override +    protected void onDetachedFromWindow() { +        super.onDetachedFromWindow(); + +        if (mPopup != null && mPopup.isShowing()) { +            mPopup.dismiss(); +        } +    } + +    /** +     * <p>A spinner does not support item click events. Calling this method +     * will raise an exception.</p> +     * +     * @param l this listener will be ignored +     */ +    @Override +    public void setOnItemClickListener(OnItemClickListener l) { +        throw new RuntimeException("setOnItemClickListener cannot be used with a spinner."); +    } + +    @Override +    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        super.onMeasure(widthMeasureSpec, heightMeasureSpec); +        if (mPopup != null && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) { +            final int measuredWidth = getMeasuredWidth(); +            setMeasuredDimension(Math.min(Math.max(measuredWidth, +                    measureContentWidth(getAdapter(), getBackground())), +                    MeasureSpec.getSize(widthMeasureSpec)), +                    getMeasuredHeight()); +        } +    } + +    /** +     * @see android.view.View#onLayout(boolean,int,int,int,int) +     * +     * Creates and positions all views +     * +     */ +    @Override +    protected void onLayout(boolean changed, int l, int t, int r, int b) { +        super.onLayout(changed, l, t, r, b); +        mInLayout = true; +        layout(0, false); +        mInLayout = false; +    } + +    /** +     * Creates and positions all views for this Spinner. +     * +     * @param delta Change in the selected position. +1 moves selection is moving to the right, +     * so views are scrolling to the left. -1 means selection is moving to the left. +     */ +    @Override +    void layout(int delta, boolean animate) { +        int childrenLeft = mSpinnerPadding.left; +        int childrenWidth = getRight() - getLeft() - mSpinnerPadding.left - mSpinnerPadding.right; + +        if (mDataChanged) { +            handleDataChanged(); +        } + +        // Handle the empty set by removing all views +        if (mItemCount == 0) { +            resetList(); +            return; +        } + +        if (mNextSelectedPosition >= 0) { +            setSelectedPositionInt(mNextSelectedPosition); +        } + +        recycleAllViews(); + +        // Clear out old views +        removeAllViewsInLayout(); + +        // Make selected view and position it +        mFirstPosition = mSelectedPosition; +        View sel = makeAndAddView(mSelectedPosition); +        int width = sel.getMeasuredWidth(); +        int selectedOffset = childrenLeft; +        switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { +            case Gravity.CENTER_HORIZONTAL: +                selectedOffset = childrenLeft + (childrenWidth / 2) - (width / 2); +                break; +            case Gravity.RIGHT: +                selectedOffset = childrenLeft + childrenWidth - width; +                break; +        } +        sel.offsetLeftAndRight(selectedOffset); + +        // Flush any cached views that did not get reused above +        mRecycler.clear(); + +        invalidate(); + +        checkSelectionChanged(); + +        mDataChanged = false; +        mNeedSync = false; +        setNextSelectedPositionInt(mSelectedPosition); +    } + +    /** +     * Obtain a view, either by pulling an existing view from the recycler or +     * by getting a new one from the adapter. If we are animating, make sure +     * there is enough information in the view's layout parameters to animate +     * from the old to new positions. +     * +     * @param position Position in the spinner for the view to obtain +     * @return A view that has been added to the spinner +     */ +    private View makeAndAddView(int position) { + +        View child; + +        if (!mDataChanged) { +            child = mRecycler.get(position); +            if (child != null) { +                // Position the view +                setUpChild(child); + +                return child; +            } +        } + +        // Nothing found in the recycler -- ask the adapter for a view +        child = mAdapter.getView(position, null, this); + +        // Position the view +        setUpChild(child); + +        return child; +    } + +    /** +     * Helper for makeAndAddView to set the position of a view +     * and fill out its layout paramters. +     * +     * @param child The view to position +     */ +    private void setUpChild(View child) { + +        // Respect layout params that are already in the view. Otherwise +        // make some up... +        ViewGroup.LayoutParams lp = child.getLayoutParams(); +        if (lp == null) { +            lp = generateDefaultLayoutParams(); +        } + +        addViewInLayout(child, 0, lp); + +        child.setSelected(hasFocus()); +        if (mDisableChildrenWhenDisabled) { +            child.setEnabled(isEnabled()); +        } + +        // Get measure specs +        int childHeightSpec = ViewGroup.getChildMeasureSpec(mHeightMeasureSpec, +                mSpinnerPadding.top + mSpinnerPadding.bottom, lp.height); +        int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec, +                mSpinnerPadding.left + mSpinnerPadding.right, lp.width); + +        // Measure child +        child.measure(childWidthSpec, childHeightSpec); + +        int childLeft; +        int childRight; + +        // Position vertically based on gravity setting +        int childTop = mSpinnerPadding.top +                + ((getMeasuredHeight() - mSpinnerPadding.bottom - +                        mSpinnerPadding.top - child.getMeasuredHeight()) / 2); +        int childBottom = childTop + child.getMeasuredHeight(); + +        int width = child.getMeasuredWidth(); +        childLeft = 0; +        childRight = childLeft + width; + +        child.layout(childLeft, childTop, childRight, childBottom); +    } + +    @Override +    public boolean performClick() { +        boolean handled = super.performClick(); + +        if (!handled) { +            handled = true; + +            if (!mPopup.isShowing()) { +                mPopup.show(); +            } +        } + +        return handled; +    } + +    public void onClick(DialogInterface dialog, int which) { +        setSelection(which); +        dialog.dismiss(); +    } + +    /** +     * Sets the prompt to display when the dialog is shown. +     * @param prompt the prompt to set +     */ +    public void setPrompt(CharSequence prompt) { +        mPopup.setPromptText(prompt); +    } + +    /** +     * Sets the prompt to display when the dialog is shown. +     * @param promptId the resource ID of the prompt to display when the dialog is shown +     */ +    public void setPromptId(int promptId) { +        setPrompt(getContext().getText(promptId)); +    } + +    /** +     * @return The prompt to display when the dialog is shown +     */ +    public CharSequence getPrompt() { +        return mPopup.getHintText(); +    } + +    int measureContentWidth(SpinnerAdapter adapter, Drawable background) { +        if (adapter == null) { +            return 0; +        } + +        int width = 0; +        View itemView = null; +        int itemType = 0; +        final int widthMeasureSpec = +            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); +        final int heightMeasureSpec = +            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + +        // Make sure the number of items we'll measure is capped. If it's a huge data set +        // with wildly varying sizes, oh well. +        int start = Math.max(0, getSelectedItemPosition()); +        final int end = Math.min(adapter.getCount(), start + MAX_ITEMS_MEASURED); +        final int count = end - start; +        start = Math.max(0, start - (MAX_ITEMS_MEASURED - count)); +        for (int i = start; i < end; i++) { +            final int positionType = adapter.getItemViewType(i); +            if (positionType != itemType) { +                itemType = positionType; +                itemView = null; +            } +            itemView = adapter.getView(i, itemView, this); +            if (itemView.getLayoutParams() == null) { +                itemView.setLayoutParams(new ViewGroup.LayoutParams( +                        ViewGroup.LayoutParams.WRAP_CONTENT, +                        ViewGroup.LayoutParams.WRAP_CONTENT)); +            } +            itemView.measure(widthMeasureSpec, heightMeasureSpec); +            width = Math.max(width, itemView.getMeasuredWidth()); +        } + +        // Add background padding to measured width +        if (background != null) { +            background.getPadding(mTempRect); +            width += mTempRect.left + mTempRect.right; +        } + +        return width; +    } + +    /** +     * <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance +     * into a ListAdapter.</p> +     */ +    private static class DropDownAdapter implements ListAdapter, SpinnerAdapter { +        private SpinnerAdapter mAdapter; +        private ListAdapter mListAdapter; + +        /** +         * <p>Creates a new ListAdapter wrapper for the specified adapter.</p> +         * +         * @param adapter the Adapter to transform into a ListAdapter +         */ +        public DropDownAdapter(SpinnerAdapter adapter) { +            this.mAdapter = adapter; +            if (adapter instanceof ListAdapter) { +                this.mListAdapter = (ListAdapter) adapter; +            } +        } + +        public int getCount() { +            return mAdapter == null ? 0 : mAdapter.getCount(); +        } + +        public Object getItem(int position) { +            return mAdapter == null ? null : mAdapter.getItem(position); +        } + +        public long getItemId(int position) { +            return mAdapter == null ? -1 : mAdapter.getItemId(position); +        } + +        public View getView(int position, View convertView, ViewGroup parent) { +            return getDropDownView(position, convertView, parent); +        } + +        public View getDropDownView(int position, View convertView, ViewGroup parent) { +            return mAdapter == null ? null : +                    mAdapter.getDropDownView(position, convertView, parent); +        } + +        public boolean hasStableIds() { +            return mAdapter != null && mAdapter.hasStableIds(); +        } + +        public void registerDataSetObserver(DataSetObserver observer) { +            if (mAdapter != null) { +                mAdapter.registerDataSetObserver(observer); +            } +        } + +        public void unregisterDataSetObserver(DataSetObserver observer) { +            if (mAdapter != null) { +                mAdapter.unregisterDataSetObserver(observer); +            } +        } + +        /** +         * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call. +         * Otherwise, return true. +         */ +        public boolean areAllItemsEnabled() { +            final ListAdapter adapter = mListAdapter; +            if (adapter != null) { +                return adapter.areAllItemsEnabled(); +            } else { +                return true; +            } +        } + +        /** +         * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call. +         * Otherwise, return true. +         */ +        public boolean isEnabled(int position) { +            final ListAdapter adapter = mListAdapter; +            if (adapter != null) { +                return adapter.isEnabled(position); +            } else { +                return true; +            } +        } + +        public int getItemViewType(int position) { +            return 0; +        } + +        public int getViewTypeCount() { +            return 1; +        } + +        public boolean isEmpty() { +            return getCount() == 0; +        } +    } + +    /** +     * Implements some sort of popup selection interface for selecting a spinner option. +     * Allows for different spinner modes. +     */ +    private interface SpinnerPopup { +        public void setAdapter(ListAdapter adapter); + +        /** +         * Show the popup +         */ +        public void show(); + +        /** +         * Dismiss the popup +         */ +        public void dismiss(); + +        /** +         * @return true if the popup is showing, false otherwise. +         */ +        public boolean isShowing(); + +        /** +         * Set hint text to be displayed to the user. This should provide +         * a description of the choice being made. +         * @param hintText Hint text to set. +         */ +        public void setPromptText(CharSequence hintText); +        public CharSequence getHintText(); +    } + +    /* +    private class DialogPopup implements SpinnerPopup, DialogInterface.OnClickListener { +        private AlertDialog mPopup; +        private ListAdapter mListAdapter; +        private CharSequence mPrompt; + +        public void dismiss() { +            mPopup.dismiss(); +            mPopup = null; +        } + +        public boolean isShowing() { +            return mPopup != null ? mPopup.isShowing() : false; +        } + +        public void setAdapter(ListAdapter adapter) { +            mListAdapter = adapter; +        } + +        public void setPromptText(CharSequence hintText) { +            mPrompt = hintText; +        } + +        public CharSequence getHintText() { +            return mPrompt; +        } + +        public void show() { +            AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); +            if (mPrompt != null) { +                builder.setTitle(mPrompt); +            } +            mPopup = builder.setSingleChoiceItems(mListAdapter, +                    getSelectedItemPosition(), this).show(); +        } + +        public void onClick(DialogInterface dialog, int which) { +            setSelection(which); +            dismiss(); +        } +    } +    */ + +    private class DropdownPopup extends IcsListPopupWindow implements SpinnerPopup { +        private CharSequence mHintText; +        private ListAdapter mAdapter; + +        public DropdownPopup(Context context, AttributeSet attrs, int defStyleRes) { +            super(context, attrs, 0, defStyleRes); + +            setAnchorView(IcsSpinner.this); +            setModal(true); +            setPromptPosition(POSITION_PROMPT_ABOVE); +            setOnItemClickListener(new OnItemClickListener() { +                @SuppressWarnings("rawtypes") +                public void onItemClick(AdapterView parent, View v, int position, long id) { +                    IcsSpinner.this.setSelection(position); +                    dismiss(); +                } +            }); +        } + +        @Override +        public void setAdapter(ListAdapter adapter) { +            super.setAdapter(adapter); +            mAdapter = adapter; +        } + +        public CharSequence getHintText() { +            return mHintText; +        } + +        public void setPromptText(CharSequence hintText) { +            // Hint text is ignored for dropdowns, but maintain it here. +            mHintText = hintText; +        } + +        @Override +        public void show() { +            final int spinnerPaddingLeft = IcsSpinner.this.getPaddingLeft(); +            if (mDropDownWidth == WRAP_CONTENT) { +                final int spinnerWidth = IcsSpinner.this.getWidth(); +                final int spinnerPaddingRight = IcsSpinner.this.getPaddingRight(); +                setContentWidth(Math.max( +                        measureContentWidth((SpinnerAdapter) mAdapter, getBackground()), +                        spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight)); +            } else if (mDropDownWidth == MATCH_PARENT) { +                final int spinnerWidth = IcsSpinner.this.getWidth(); +                final int spinnerPaddingRight = IcsSpinner.this.getPaddingRight(); +                setContentWidth(spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight); +            } else { +                setContentWidth(mDropDownWidth); +            } +            final Drawable background = getBackground(); +            int bgOffset = 0; +            if (background != null) { +                background.getPadding(mTempRect); +                bgOffset = -mTempRect.left; +            } +            setHorizontalOffset(bgOffset + spinnerPaddingLeft); +            setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); +            super.show(); +            getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); +            setSelection(IcsSpinner.this.getSelectedItemPosition()); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsView.java new file mode 100644 index 000000000..a7185d082 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsView.java @@ -0,0 +1,21 @@ +package com.actionbarsherlock.internal.widget; + +import android.view.View; + +final class IcsView { +    //No instances +    private IcsView() {} + +    /** +     * Return only the state bits of {@link #getMeasuredWidthAndState()} +     * and {@link #getMeasuredHeightAndState()}, combined into one integer. +     * The width component is in the regular bits {@link #MEASURED_STATE_MASK} +     * and the height component is at the shifted bits +     * {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}. +     */ +    public static int getMeasuredStateInt(View child) { +        return (child.getMeasuredWidth()&View.MEASURED_STATE_MASK) +                | ((child.getMeasuredHeight()>>View.MEASURED_HEIGHT_STATE_SHIFT) +                        & (View.MEASURED_STATE_MASK>>View.MEASURED_HEIGHT_STATE_SHIFT)); +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java new file mode 100644 index 000000000..48fb5d8b4 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java @@ -0,0 +1,546 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.actionbarsherlock.internal.widget; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.text.TextUtils.TruncateAt; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import com.actionbarsherlock.R; +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.internal.nineoldandroids.animation.Animator; +import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator; +import com.actionbarsherlock.internal.nineoldandroids.widget.NineHorizontalScrollView; + +/** + * This widget implements the dynamic action bar tab behavior that can change + * across different configurations or circumstances. + */ +public class ScrollingTabContainerView extends NineHorizontalScrollView +        implements IcsAdapterView.OnItemSelectedListener { +    //UNUSED private static final String TAG = "ScrollingTabContainerView"; +    Runnable mTabSelector; +    private TabClickListener mTabClickListener; + +    private IcsLinearLayout mTabLayout; +    private IcsSpinner mTabSpinner; +    private boolean mAllowCollapse; + +    private LayoutInflater mInflater; + +    int mMaxTabWidth; +    private int mContentHeight; +    private int mSelectedTabIndex; + +    protected Animator mVisibilityAnim; +    protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener(); + +    private static final /*Time*/Interpolator sAlphaInterpolator = new DecelerateInterpolator(); + +    private static final int FADE_DURATION = 200; + +    public ScrollingTabContainerView(Context context) { +        super(context); +        setHorizontalScrollBarEnabled(false); + +        TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar, +                R.attr.actionBarStyle, 0); +        setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0)); +        a.recycle(); + +        mInflater = LayoutInflater.from(context); + +        mTabLayout = createTabLayout(); +        addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, +                ViewGroup.LayoutParams.MATCH_PARENT)); +    } + +    @Override +    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        final int widthMode = MeasureSpec.getMode(widthMeasureSpec); +        final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY; +        setFillViewport(lockedExpanded); + +        final int childCount = mTabLayout.getChildCount(); +        if (childCount > 1 && +                (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) { +            if (childCount > 2) { +                mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f); +            } else { +                mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2; +            } +        } else { +            mMaxTabWidth = -1; +        } + +        heightMeasureSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY); + +        final boolean canCollapse = !lockedExpanded && mAllowCollapse; + +        if (canCollapse) { +            // See if we should expand +            mTabLayout.measure(MeasureSpec.UNSPECIFIED, heightMeasureSpec); +            if (mTabLayout.getMeasuredWidth() > MeasureSpec.getSize(widthMeasureSpec)) { +                performCollapse(); +            } else { +                performExpand(); +            } +        } else { +            performExpand(); +        } + +        final int oldWidth = getMeasuredWidth(); +        super.onMeasure(widthMeasureSpec, heightMeasureSpec); +        final int newWidth = getMeasuredWidth(); + +        if (lockedExpanded && oldWidth != newWidth) { +            // Recenter the tab display if we're at a new (scrollable) size. +            setTabSelected(mSelectedTabIndex); +        } +    } + +    /** +     * Indicates whether this view is collapsed into a dropdown menu instead +     * of traditional tabs. +     * @return true if showing as a spinner +     */ +    private boolean isCollapsed() { +        return mTabSpinner != null && mTabSpinner.getParent() == this; +    } + +    public void setAllowCollapse(boolean allowCollapse) { +        mAllowCollapse = allowCollapse; +    } + +    private void performCollapse() { +        if (isCollapsed()) return; + +        if (mTabSpinner == null) { +            mTabSpinner = createSpinner(); +        } +        removeView(mTabLayout); +        addView(mTabSpinner, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, +                ViewGroup.LayoutParams.MATCH_PARENT)); +        if (mTabSpinner.getAdapter() == null) { +            mTabSpinner.setAdapter(new TabAdapter()); +        } +        if (mTabSelector != null) { +            removeCallbacks(mTabSelector); +            mTabSelector = null; +        } +        mTabSpinner.setSelection(mSelectedTabIndex); +    } + +    private boolean performExpand() { +        if (!isCollapsed()) return false; + +        removeView(mTabSpinner); +        addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, +                ViewGroup.LayoutParams.MATCH_PARENT)); +        setTabSelected(mTabSpinner.getSelectedItemPosition()); +        return false; +    } + +    public void setTabSelected(int position) { +        mSelectedTabIndex = position; +        final int tabCount = mTabLayout.getChildCount(); +        for (int i = 0; i < tabCount; i++) { +            final View child = mTabLayout.getChildAt(i); +            final boolean isSelected = i == position; +            child.setSelected(isSelected); +            if (isSelected) { +                animateToTab(position); +            } +        } +    } + +    public void setContentHeight(int contentHeight) { +        mContentHeight = contentHeight; +        requestLayout(); +    } + +    private IcsLinearLayout createTabLayout() { +        final IcsLinearLayout tabLayout = (IcsLinearLayout) LayoutInflater.from(getContext()) +                .inflate(R.layout.abs__action_bar_tab_bar_view, null); +        tabLayout.setMeasureWithLargestChildEnabled(true); +        tabLayout.setLayoutParams(new LinearLayout.LayoutParams( +                LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT)); +        return tabLayout; +    } + +    private IcsSpinner createSpinner() { +        final IcsSpinner spinner = new IcsSpinner(getContext(), null, +                R.attr.actionDropDownStyle); +        spinner.setLayoutParams(new LinearLayout.LayoutParams( +                LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT)); +        spinner.setOnItemSelectedListener(this); +        return spinner; +    } + +    @Override +    protected void onConfigurationChanged(Configuration newConfig) { +        super.onConfigurationChanged(newConfig); + +        // Action bar can change size on configuration changes. +        // Reread the desired height from the theme-specified style. +        TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar, +                R.attr.actionBarStyle, 0); +        setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0)); +        a.recycle(); +    } + +    public void animateToVisibility(int visibility) { +        if (mVisibilityAnim != null) { +            mVisibilityAnim.cancel(); +        } +        if (visibility == VISIBLE) { +            if (getVisibility() != VISIBLE) { +                setAlpha(0); +            } +            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1); +            anim.setDuration(FADE_DURATION); +            anim.setInterpolator(sAlphaInterpolator); + +            anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); +            anim.start(); +        } else { +            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0); +            anim.setDuration(FADE_DURATION); +            anim.setInterpolator(sAlphaInterpolator); + +            anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); +            anim.start(); +        } +    } + +    public void animateToTab(final int position) { +        final View tabView = mTabLayout.getChildAt(position); +        if (mTabSelector != null) { +            removeCallbacks(mTabSelector); +        } +        mTabSelector = new Runnable() { +            public void run() { +                final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2; +                smoothScrollTo(scrollPos, 0); +                mTabSelector = null; +            } +        }; +        post(mTabSelector); +    } + +    @Override +    public void onAttachedToWindow() { +        super.onAttachedToWindow(); +        if (mTabSelector != null) { +            // Re-post the selector we saved +            post(mTabSelector); +        } +    } + +    @Override +    public void onDetachedFromWindow() { +        super.onDetachedFromWindow(); +        if (mTabSelector != null) { +            removeCallbacks(mTabSelector); +        } +    } + +    private TabView createTabView(ActionBar.Tab tab, boolean forAdapter) { +        //Workaround for not being able to pass a defStyle on pre-3.0 +        final TabView tabView = (TabView)mInflater.inflate(R.layout.abs__action_bar_tab, null); +        tabView.init(this, tab, forAdapter); + +        if (forAdapter) { +            tabView.setBackgroundDrawable(null); +            tabView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT, +                    mContentHeight)); +        } else { +            tabView.setFocusable(true); + +            if (mTabClickListener == null) { +                mTabClickListener = new TabClickListener(); +            } +            tabView.setOnClickListener(mTabClickListener); +        } +        return tabView; +    } + +    public void addTab(ActionBar.Tab tab, boolean setSelected) { +        TabView tabView = createTabView(tab, false); +        mTabLayout.addView(tabView, new IcsLinearLayout.LayoutParams(0, +                LayoutParams.MATCH_PARENT, 1)); +        if (mTabSpinner != null) { +            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged(); +        } +        if (setSelected) { +            tabView.setSelected(true); +        } +        if (mAllowCollapse) { +            requestLayout(); +        } +    } + +    public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { +        final TabView tabView = createTabView(tab, false); +        mTabLayout.addView(tabView, position, new IcsLinearLayout.LayoutParams( +                0, LayoutParams.MATCH_PARENT, 1)); +        if (mTabSpinner != null) { +            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged(); +        } +        if (setSelected) { +            tabView.setSelected(true); +        } +        if (mAllowCollapse) { +            requestLayout(); +        } +    } + +    public void updateTab(int position) { +        ((TabView) mTabLayout.getChildAt(position)).update(); +        if (mTabSpinner != null) { +            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged(); +        } +        if (mAllowCollapse) { +            requestLayout(); +        } +    } + +    public void removeTabAt(int position) { +        mTabLayout.removeViewAt(position); +        if (mTabSpinner != null) { +            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged(); +        } +        if (mAllowCollapse) { +            requestLayout(); +        } +    } + +    public void removeAllTabs() { +        mTabLayout.removeAllViews(); +        if (mTabSpinner != null) { +            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged(); +        } +        if (mAllowCollapse) { +            requestLayout(); +        } +    } + +    @Override +    public void onItemSelected(IcsAdapterView<?> parent, View view, int position, long id) { +        TabView tabView = (TabView) view; +        tabView.getTab().select(); +    } + +    @Override +    public void onNothingSelected(IcsAdapterView<?> parent) { +    } + +    public static class TabView extends LinearLayout { +        private ScrollingTabContainerView mParent; +        private ActionBar.Tab mTab; +        private CapitalizingTextView mTextView; +        private ImageView mIconView; +        private View mCustomView; + +        public TabView(Context context, AttributeSet attrs) { +            //TODO super(context, null, R.attr.actionBarTabStyle); +            super(context, attrs); +        } + +        public void init(ScrollingTabContainerView parent, ActionBar.Tab tab, boolean forList) { +            mParent = parent; +            mTab = tab; + +            if (forList) { +                setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); +            } + +            update(); +        } + +        public void bindTab(ActionBar.Tab tab) { +            mTab = tab; +            update(); +        } + +        @Override +        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +            super.onMeasure(widthMeasureSpec, heightMeasureSpec); + +            // Re-measure if we went beyond our maximum size. +            if (mParent.mMaxTabWidth > 0 && getMeasuredWidth() > mParent.mMaxTabWidth) { +                super.onMeasure(MeasureSpec.makeMeasureSpec(mParent.mMaxTabWidth, MeasureSpec.EXACTLY), +                        heightMeasureSpec); +            } +        } + +        public void update() { +            final ActionBar.Tab tab = mTab; +            final View custom = tab.getCustomView(); +            if (custom != null) { +                final ViewParent customParent = custom.getParent(); +                if (customParent != this) { +                    if (customParent != null) ((ViewGroup) customParent).removeView(custom); +                    addView(custom); +                } +                mCustomView = custom; +                if (mTextView != null) mTextView.setVisibility(GONE); +                if (mIconView != null) { +                    mIconView.setVisibility(GONE); +                    mIconView.setImageDrawable(null); +                } +            } else { +                if (mCustomView != null) { +                    removeView(mCustomView); +                    mCustomView = null; +                } + +                final Drawable icon = tab.getIcon(); +                final CharSequence text = tab.getText(); + +                if (icon != null) { +                    if (mIconView == null) { +                        ImageView iconView = new ImageView(getContext()); +                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, +                                LayoutParams.WRAP_CONTENT); +                        lp.gravity = Gravity.CENTER_VERTICAL; +                        iconView.setLayoutParams(lp); +                        addView(iconView, 0); +                        mIconView = iconView; +                    } +                    mIconView.setImageDrawable(icon); +                    mIconView.setVisibility(VISIBLE); +                } else if (mIconView != null) { +                    mIconView.setVisibility(GONE); +                    mIconView.setImageDrawable(null); +                } + +                if (text != null) { +                    if (mTextView == null) { +                        CapitalizingTextView textView = new CapitalizingTextView(getContext(), null, +                                R.attr.actionBarTabTextStyle); +                        textView.setEllipsize(TruncateAt.END); +                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, +                                LayoutParams.WRAP_CONTENT); +                        lp.gravity = Gravity.CENTER_VERTICAL; +                        textView.setLayoutParams(lp); +                        addView(textView); +                        mTextView = textView; +                    } +                    mTextView.setTextCompat(text); +                    mTextView.setVisibility(VISIBLE); +                } else if (mTextView != null) { +                    mTextView.setVisibility(GONE); +                    mTextView.setText(null); +                } + +                if (mIconView != null) { +                    mIconView.setContentDescription(tab.getContentDescription()); +                } +            } +        } + +        public ActionBar.Tab getTab() { +            return mTab; +        } +    } + +    private class TabAdapter extends BaseAdapter { +        @Override +        public int getCount() { +            return mTabLayout.getChildCount(); +        } + +        @Override +        public Object getItem(int position) { +            return ((TabView) mTabLayout.getChildAt(position)).getTab(); +        } + +        @Override +        public long getItemId(int position) { +            return position; +        } + +        @Override +        public View getView(int position, View convertView, ViewGroup parent) { +            if (convertView == null) { +                convertView = createTabView((ActionBar.Tab) getItem(position), true); +            } else { +                ((TabView) convertView).bindTab((ActionBar.Tab) getItem(position)); +            } +            return convertView; +        } +    } + +    private class TabClickListener implements OnClickListener { +        public void onClick(View view) { +            TabView tabView = (TabView) view; +            tabView.getTab().select(); +            final int tabCount = mTabLayout.getChildCount(); +            for (int i = 0; i < tabCount; i++) { +                final View child = mTabLayout.getChildAt(i); +                child.setSelected(child == view); +            } +        } +    } + +    protected class VisibilityAnimListener implements Animator.AnimatorListener { +        private boolean mCanceled = false; +        private int mFinalVisibility; + +        public VisibilityAnimListener withFinalVisibility(int visibility) { +            mFinalVisibility = visibility; +            return this; +        } + +        @Override +        public void onAnimationStart(Animator animation) { +            setVisibility(VISIBLE); +            mVisibilityAnim = animation; +            mCanceled = false; +        } + +        @Override +        public void onAnimationEnd(Animator animation) { +            if (mCanceled) return; + +            mVisibilityAnim = null; +            setVisibility(mFinalVisibility); +        } + +        @Override +        public void onAnimationCancel(Animator animation) { +            mCanceled = true; +        } + +        @Override +        public void onAnimationRepeat(Animator animation) { +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/ActionMode.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/ActionMode.java new file mode 100644 index 000000000..81b4cd4d2 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/ActionMode.java @@ -0,0 +1,224 @@ +/*
 + * Copyright (C) 2010 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.actionbarsherlock.view;
 +
 +import android.view.View;
 +
 +
 +/**
 + * Represents a contextual mode of the user interface. Action modes can be used for
 + * modal interactions with content and replace parts of the normal UI until finished.
 + * Examples of good action modes include selection modes, search, content editing, etc.
 + */
 +public abstract class ActionMode {
 +    private Object mTag;
 +
 +    /**
 +     * Set a tag object associated with this ActionMode.
 +     *
 +     * <p>Like the tag available to views, this allows applications to associate arbitrary
 +     * data with an ActionMode for later reference.
 +     *
 +     * @param tag Tag to associate with this ActionMode
 +     *
 +     * @see #getTag()
 +     */
 +    public void setTag(Object tag) {
 +        mTag = tag;
 +    }
 +
 +    /**
 +     * Retrieve the tag object associated with this ActionMode.
 +     *
 +     * <p>Like the tag available to views, this allows applications to associate arbitrary
 +     * data with an ActionMode for later reference.
 +     *
 +     * @return Tag associated with this ActionMode
 +     *
 +     * @see #setTag(Object)
 +     */
 +    public Object getTag() {
 +        return mTag;
 +    }
 +
 +    /**
 +     * Set the title of the action mode. This method will have no visible effect if
 +     * a custom view has been set.
 +     *
 +     * @param title Title string to set
 +     *
 +     * @see #setTitle(int)
 +     * @see #setCustomView(View)
 +     */
 +    public abstract void setTitle(CharSequence title);
 +
 +    /**
 +     * Set the title of the action mode. This method will have no visible effect if
 +     * a custom view has been set.
 +     *
 +     * @param resId Resource ID of a string to set as the title
 +     *
 +     * @see #setTitle(CharSequence)
 +     * @see #setCustomView(View)
 +     */
 +    public abstract void setTitle(int resId);
 +
 +    /**
 +     * Set the subtitle of the action mode. This method will have no visible effect if
 +     * a custom view has been set.
 +     *
 +     * @param subtitle Subtitle string to set
 +     *
 +     * @see #setSubtitle(int)
 +     * @see #setCustomView(View)
 +     */
 +    public abstract void setSubtitle(CharSequence subtitle);
 +
 +    /**
 +     * Set the subtitle of the action mode. This method will have no visible effect if
 +     * a custom view has been set.
 +     *
 +     * @param resId Resource ID of a string to set as the subtitle
 +     *
 +     * @see #setSubtitle(CharSequence)
 +     * @see #setCustomView(View)
 +     */
 +    public abstract void setSubtitle(int resId);
 +
 +    /**
 +     * Set a custom view for this action mode. The custom view will take the place of
 +     * the title and subtitle. Useful for things like search boxes.
 +     *
 +     * @param view Custom view to use in place of the title/subtitle.
 +     *
 +     * @see #setTitle(CharSequence)
 +     * @see #setSubtitle(CharSequence)
 +     */
 +    public abstract void setCustomView(View view);
 +
 +    /**
 +     * Invalidate the action mode and refresh menu content. The mode's
 +     * {@link ActionMode.Callback} will have its
 +     * {@link Callback#onPrepareActionMode(ActionMode, Menu)} method called.
 +     * If it returns true the menu will be scanned for updated content and any relevant changes
 +     * will be reflected to the user.
 +     */
 +    public abstract void invalidate();
 +
 +    /**
 +     * Finish and close this action mode. The action mode's {@link ActionMode.Callback} will
 +     * have its {@link Callback#onDestroyActionMode(ActionMode)} method called.
 +     */
 +    public abstract void finish();
 +
 +    /**
 +     * Returns the menu of actions that this action mode presents.
 +     * @return The action mode's menu.
 +     */
 +    public abstract Menu getMenu();
 +
 +    /**
 +     * Returns the current title of this action mode.
 +     * @return Title text
 +     */
 +    public abstract CharSequence getTitle();
 +
 +    /**
 +     * Returns the current subtitle of this action mode.
 +     * @return Subtitle text
 +     */
 +    public abstract CharSequence getSubtitle();
 +
 +    /**
 +     * Returns the current custom view for this action mode.
 +     * @return The current custom view
 +     */
 +    public abstract View getCustomView();
 +
 +    /**
 +     * Returns a {@link MenuInflater} with the ActionMode's context.
 +     */
 +    public abstract MenuInflater getMenuInflater();
 +
 +    /**
 +     * Returns whether the UI presenting this action mode can take focus or not.
 +     * This is used by internal components within the framework that would otherwise
 +     * present an action mode UI that requires focus, such as an EditText as a custom view.
 +     *
 +     * @return true if the UI used to show this action mode can take focus
 +     * @hide Internal use only
 +     */
 +    public boolean isUiFocusable() {
 +        return true;
 +    }
 +
 +    /**
 +     * Callback interface for action modes. Supplied to
 +     * {@link View#startActionMode(Callback)}, a Callback
 +     * configures and handles events raised by a user's interaction with an action mode.
 +     *
 +     * <p>An action mode's lifecycle is as follows:
 +     * <ul>
 +     * <li>{@link Callback#onCreateActionMode(ActionMode, Menu)} once on initial
 +     * creation</li>
 +     * <li>{@link Callback#onPrepareActionMode(ActionMode, Menu)} after creation
 +     * and any time the {@link ActionMode} is invalidated</li>
 +     * <li>{@link Callback#onActionItemClicked(ActionMode, MenuItem)} any time a
 +     * contextual action button is clicked</li>
 +     * <li>{@link Callback#onDestroyActionMode(ActionMode)} when the action mode
 +     * is closed</li>
 +     * </ul>
 +     */
 +    public interface Callback {
 +        /**
 +         * Called when action mode is first created. The menu supplied will be used to
 +         * generate action buttons for the action mode.
 +         *
 +         * @param mode ActionMode being created
 +         * @param menu Menu used to populate action buttons
 +         * @return true if the action mode should be created, false if entering this
 +         *              mode should be aborted.
 +         */
 +        public boolean onCreateActionMode(ActionMode mode, Menu menu);
 +
 +        /**
 +         * Called to refresh an action mode's action menu whenever it is invalidated.
 +         *
 +         * @param mode ActionMode being prepared
 +         * @param menu Menu used to populate action buttons
 +         * @return true if the menu or action mode was updated, false otherwise.
 +         */
 +        public boolean onPrepareActionMode(ActionMode mode, Menu menu);
 +
 +        /**
 +         * Called to report a user click on an action button.
 +         *
 +         * @param mode The current ActionMode
 +         * @param item The item that was clicked
 +         * @return true if this callback handled the event, false if the standard MenuItem
 +         *          invocation should continue.
 +         */
 +        public boolean onActionItemClicked(ActionMode mode, MenuItem item);
 +
 +        /**
 +         * Called when an action mode is about to be exited and destroyed.
 +         *
 +         * @param mode The current ActionMode being destroyed
 +         */
 +        public void onDestroyActionMode(ActionMode mode);
 +    }
 +}
\ No newline at end of file diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/ActionProvider.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/ActionProvider.java new file mode 100644 index 000000000..ae7cb1fe0 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/ActionProvider.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.actionbarsherlock.view; + +import android.content.Context; +import android.view.View; + +/** + * This class is a mediator for accomplishing a given task, for example sharing a file. + * It is responsible for creating a view that performs an action that accomplishes the task. + * This class also implements other functions such a performing a default action. + * <p> + * An ActionProvider can be optionally specified for a {@link MenuItem} and in such a + * case it will be responsible for creating the action view that appears in the + * {@link android.app.ActionBar} as a substitute for the menu item when the item is + * displayed as an action item. Also the provider is responsible for performing a + * default action if a menu item placed on the overflow menu of the ActionBar is + * selected and none of the menu item callbacks has handled the selection. For this + * case the provider can also optionally provide a sub-menu for accomplishing the + * task at hand. + * </p> + * <p> + * There are two ways for using an action provider for creating and handling of action views: + * <ul> + * <li> + * Setting the action provider on a {@link MenuItem} directly by calling + * {@link MenuItem#setActionProvider(ActionProvider)}. + * </li> + * <li> + * Declaring the action provider in the menu XML resource. For example: + * <pre> + * <code> + *   <item android:id="@+id/my_menu_item" + *     android:title="Title" + *     android:icon="@drawable/my_menu_item_icon" + *     android:showAsAction="ifRoom" + *     android:actionProviderClass="foo.bar.SomeActionProvider" /> + * </code> + * </pre> + * </li> + * </ul> + * </p> + * + * @see MenuItem#setActionProvider(ActionProvider) + * @see MenuItem#getActionProvider() + */ +public abstract class ActionProvider { +    private SubUiVisibilityListener mSubUiVisibilityListener; + +    /** +     * Creates a new instance. +     * +     * @param context Context for accessing resources. +     */ +    public ActionProvider(Context context) { +    } + +    /** +     * Factory method for creating new action views. +     * +     * @return A new action view. +     */ +    public abstract View onCreateActionView(); + +    /** +     * Performs an optional default action. +     * <p> +     * For the case of an action provider placed in a menu item not shown as an action this +     * method is invoked if previous callbacks for processing menu selection has handled +     * the event. +     * </p> +     * <p> +     * A menu item selection is processed in the following order: +     * <ul> +     * <li> +     * Receiving a call to {@link MenuItem.OnMenuItemClickListener#onMenuItemClick +     *  MenuItem.OnMenuItemClickListener.onMenuItemClick}. +     * </li> +     * <li> +     * Receiving a call to {@link android.app.Activity#onOptionsItemSelected(MenuItem) +     *  Activity.onOptionsItemSelected(MenuItem)} +     * </li> +     * <li> +     * Receiving a call to {@link android.app.Fragment#onOptionsItemSelected(MenuItem) +     *  Fragment.onOptionsItemSelected(MenuItem)} +     * </li> +     * <li> +     * Launching the {@link android.content.Intent} set via +     * {@link MenuItem#setIntent(android.content.Intent) MenuItem.setIntent(android.content.Intent)} +     * </li> +     * <li> +     * Invoking this method. +     * </li> +     * </ul> +     * </p> +     * <p> +     * The default implementation does not perform any action and returns false. +     * </p> +     */ +    public boolean onPerformDefaultAction() { +        return false; +    } + +    /** +     * Determines if this ActionProvider has a submenu associated with it. +     * +     * <p>Associated submenus will be shown when an action view is not. This +     * provider instance will receive a call to {@link #onPrepareSubMenu(SubMenu)} +     * after the call to {@link #onPerformDefaultAction()} and before a submenu is +     * displayed to the user. +     * +     * @return true if the item backed by this provider should have an associated submenu +     */ +    public boolean hasSubMenu() { +        return false; +    } + +    /** +     * Called to prepare an associated submenu for the menu item backed by this ActionProvider. +     * +     * <p>if {@link #hasSubMenu()} returns true, this method will be called when the +     * menu item is selected to prepare the submenu for presentation to the user. Apps +     * may use this to create or alter submenu content right before display. +     * +     * @param subMenu Submenu that will be displayed +     */ +    public void onPrepareSubMenu(SubMenu subMenu) { +    } + +    /** +     * Notify the system that the visibility of an action view's sub-UI such as +     * an anchored popup has changed. This will affect how other system +     * visibility notifications occur. +     * +     * @hide Pending future API approval +     */ +    public void subUiVisibilityChanged(boolean isVisible) { +        if (mSubUiVisibilityListener != null) { +            mSubUiVisibilityListener.onSubUiVisibilityChanged(isVisible); +        } +    } + +    /** +     * @hide Internal use only +     */ +    public void setSubUiVisibilityListener(SubUiVisibilityListener listener) { +        mSubUiVisibilityListener = listener; +    } + +    /** +     * @hide Internal use only +     */ +    public interface SubUiVisibilityListener { +        public void onSubUiVisibilityChanged(boolean isVisible); +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/CollapsibleActionView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/CollapsibleActionView.java new file mode 100644 index 000000000..43281b013 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/CollapsibleActionView.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.actionbarsherlock.view; + +/** + * When a {@link View} implements this interface it will receive callbacks + * when expanded or collapsed as an action view alongside the optional, + * app-specified callbacks to {@link OnActionExpandListener}. + * + * <p>See {@link MenuItem} for more information about action views. + * See {@link android.app.ActionBar} for more information about the action bar. + */ +public interface CollapsibleActionView { +    /** +     * Called when this view is expanded as an action view. +     * See {@link MenuItem#expandActionView()}. +     */ +    public void onActionViewExpanded(); + +    /** +     * Called when this view is collapsed as an action view. +     * See {@link MenuItem#collapseActionView()}. +     */ +    public void onActionViewCollapsed(); +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/Menu.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/Menu.java new file mode 100644 index 000000000..951f4ccef --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/Menu.java @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2006 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.actionbarsherlock.view; + +import android.content.ComponentName; +import android.content.Intent; +import android.view.KeyEvent; + +/** + * Interface for managing the items in a menu. + * <p> + * By default, every Activity supports an options menu of actions or options. + * You can add items to this menu and handle clicks on your additions. The + * easiest way of adding menu items is inflating an XML file into the + * {@link Menu} via {@link MenuInflater}. The easiest way of attaching code to + * clicks is via {@link Activity#onOptionsItemSelected(MenuItem)} and + * {@link Activity#onContextItemSelected(MenuItem)}. + * <p> + * Different menu types support different features: + * <ol> + * <li><b>Context menus</b>: Do not support item shortcuts and item icons. + * <li><b>Options menus</b>: The <b>icon menus</b> do not support item check + * marks and only show the item's + * {@link MenuItem#setTitleCondensed(CharSequence) condensed title}. The + * <b>expanded menus</b> (only available if six or more menu items are visible, + * reached via the 'More' item in the icon menu) do not show item icons, and + * item check marks are discouraged. + * <li><b>Sub menus</b>: Do not support item icons, or nested sub menus. + * </ol> + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about creating menus, read the + * <a href="{@docRoot}guide/topics/ui/menus.html">Menus</a> developer guide.</p> + * </div> + */ +public interface Menu { + +    /** +     * This is the part of an order integer that the user can provide. +     * @hide +     */ +    static final int USER_MASK = 0x0000ffff; +    /** +     * Bit shift of the user portion of the order integer. +     * @hide +     */ +    static final int USER_SHIFT = 0; + +    /** +     * This is the part of an order integer that supplies the category of the +     * item. +     * @hide +     */ +    static final int CATEGORY_MASK = 0xffff0000; +    /** +     * Bit shift of the category portion of the order integer. +     * @hide +     */ +    static final int CATEGORY_SHIFT = 16; + +    /** +     * Value to use for group and item identifier integers when you don't care +     * about them. +     */ +    static final int NONE = 0; + +    /** +     * First value for group and item identifier integers. +     */ +    static final int FIRST = 1; + +    // Implementation note: Keep these CATEGORY_* in sync with the category enum +    // in attrs.xml + +    /** +     * Category code for the order integer for items/groups that are part of a +     * container -- or/add this with your base value. +     */ +    static final int CATEGORY_CONTAINER = 0x00010000; + +    /** +     * Category code for the order integer for items/groups that are provided by +     * the system -- or/add this with your base value. +     */ +    static final int CATEGORY_SYSTEM = 0x00020000; + +    /** +     * Category code for the order integer for items/groups that are +     * user-supplied secondary (infrequently used) options -- or/add this with +     * your base value. +     */ +    static final int CATEGORY_SECONDARY = 0x00030000; + +    /** +     * Category code for the order integer for items/groups that are +     * alternative actions on the data that is currently displayed -- or/add +     * this with your base value. +     */ +    static final int CATEGORY_ALTERNATIVE = 0x00040000; + +    /** +     * Flag for {@link #addIntentOptions}: if set, do not automatically remove +     * any existing menu items in the same group. +     */ +    static final int FLAG_APPEND_TO_GROUP = 0x0001; + +    /** +     * Flag for {@link #performShortcut}: if set, do not close the menu after +     * executing the shortcut. +     */ +    static final int FLAG_PERFORM_NO_CLOSE = 0x0001; + +    /** +     * Flag for {@link #performShortcut(int, KeyEvent, int)}: if set, always +     * close the menu after executing the shortcut. Closing the menu also resets +     * the prepared state. +     */ +    static final int FLAG_ALWAYS_PERFORM_CLOSE = 0x0002; + +    /** +     * Add a new item to the menu. This item displays the given title for its +     * label. +     * +     * @param title The text to display for the item. +     * @return The newly added menu item. +     */ +    public MenuItem add(CharSequence title); + +    /** +     * Add a new item to the menu. This item displays the given title for its +     * label. +     * +     * @param titleRes Resource identifier of title string. +     * @return The newly added menu item. +     */ +    public MenuItem add(int titleRes); + +    /** +     * Add a new item to the menu. This item displays the given title for its +     * label. +     * +     * @param groupId The group identifier that this item should be part of. +     *        This can be used to define groups of items for batch state +     *        changes. Normally use {@link #NONE} if an item should not be in a +     *        group. +     * @param itemId Unique item ID. Use {@link #NONE} if you do not need a +     *        unique ID. +     * @param order The order for the item. Use {@link #NONE} if you do not care +     *        about the order. See {@link MenuItem#getOrder()}. +     * @param title The text to display for the item. +     * @return The newly added menu item. +     */ +    public MenuItem add(int groupId, int itemId, int order, CharSequence title); + +    /** +     * Variation on {@link #add(int, int, int, CharSequence)} that takes a +     * string resource identifier instead of the string itself. +     * +     * @param groupId The group identifier that this item should be part of. +     *        This can also be used to define groups of items for batch state +     *        changes. Normally use {@link #NONE} if an item should not be in a +     *        group. +     * @param itemId Unique item ID. Use {@link #NONE} if you do not need a +     *        unique ID. +     * @param order The order for the item. Use {@link #NONE} if you do not care +     *        about the order. See {@link MenuItem#getOrder()}. +     * @param titleRes Resource identifier of title string. +     * @return The newly added menu item. +     */ +    public MenuItem add(int groupId, int itemId, int order, int titleRes); + +    /** +     * Add a new sub-menu to the menu. This item displays the given title for +     * its label. To modify other attributes on the submenu's menu item, use +     * {@link SubMenu#getItem()}. +     * +     * @param title The text to display for the item. +     * @return The newly added sub-menu +     */ +    SubMenu addSubMenu(final CharSequence title); + +    /** +     * Add a new sub-menu to the menu. This item displays the given title for +     * its label. To modify other attributes on the submenu's menu item, use +     * {@link SubMenu#getItem()}. +     * +     * @param titleRes Resource identifier of title string. +     * @return The newly added sub-menu +     */ +    SubMenu addSubMenu(final int titleRes); + +    /** +     * Add a new sub-menu to the menu. This item displays the given +     * <var>title</var> for its label. To modify other attributes on the +     * submenu's menu item, use {@link SubMenu#getItem()}. +     *<p> +     * Note that you can only have one level of sub-menus, i.e. you cannnot add +     * a subMenu to a subMenu: An {@link UnsupportedOperationException} will be +     * thrown if you try. +     * +     * @param groupId The group identifier that this item should be part of. +     *        This can also be used to define groups of items for batch state +     *        changes. Normally use {@link #NONE} if an item should not be in a +     *        group. +     * @param itemId Unique item ID. Use {@link #NONE} if you do not need a +     *        unique ID. +     * @param order The order for the item. Use {@link #NONE} if you do not care +     *        about the order. See {@link MenuItem#getOrder()}. +     * @param title The text to display for the item. +     * @return The newly added sub-menu +     */ +    SubMenu addSubMenu(final int groupId, final int itemId, int order, final CharSequence title); + +    /** +     * Variation on {@link #addSubMenu(int, int, int, CharSequence)} that takes +     * a string resource identifier for the title instead of the string itself. +     * +     * @param groupId The group identifier that this item should be part of. +     *        This can also be used to define groups of items for batch state +     *        changes. Normally use {@link #NONE} if an item should not be in a group. +     * @param itemId Unique item ID. Use {@link #NONE} if you do not need a unique ID. +     * @param order The order for the item. Use {@link #NONE} if you do not care about the +     *        order. See {@link MenuItem#getOrder()}. +     * @param titleRes Resource identifier of title string. +     * @return The newly added sub-menu +     */ +    SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes); + +    /** +     * Add a group of menu items corresponding to actions that can be performed +     * for a particular Intent. The Intent is most often configured with a null +     * action, the data that the current activity is working with, and includes +     * either the {@link Intent#CATEGORY_ALTERNATIVE} or +     * {@link Intent#CATEGORY_SELECTED_ALTERNATIVE} to find activities that have +     * said they would like to be included as optional action. You can, however, +     * use any Intent you want. +     * +     * <p> +     * See {@link android.content.pm.PackageManager#queryIntentActivityOptions} +     * for more * details on the <var>caller</var>, <var>specifics</var>, and +     * <var>intent</var> arguments. The list returned by that function is used +     * to populate the resulting menu items. +     * +     * <p> +     * All of the menu items of possible options for the intent will be added +     * with the given group and id. You can use the group to control ordering of +     * the items in relation to other items in the menu. Normally this function +     * will automatically remove any existing items in the menu in the same +     * group and place a divider above and below the added items; this behavior +     * can be modified with the <var>flags</var> parameter. For each of the +     * generated items {@link MenuItem#setIntent} is called to associate the +     * appropriate Intent with the item; this means the activity will +     * automatically be started for you without having to do anything else. +     * +     * @param groupId The group identifier that the items should be part of. +     *        This can also be used to define groups of items for batch state +     *        changes. Normally use {@link #NONE} if the items should not be in +     *        a group. +     * @param itemId Unique item ID. Use {@link #NONE} if you do not need a +     *        unique ID. +     * @param order The order for the items. Use {@link #NONE} if you do not +     *        care about the order. See {@link MenuItem#getOrder()}. +     * @param caller The current activity component name as defined by +     *        queryIntentActivityOptions(). +     * @param specifics Specific items to place first as defined by +     *        queryIntentActivityOptions(). +     * @param intent Intent describing the kinds of items to populate in the +     *        list as defined by queryIntentActivityOptions(). +     * @param flags Additional options controlling how the items are added. +     * @param outSpecificItems Optional array in which to place the menu items +     *        that were generated for each of the <var>specifics</var> that were +     *        requested. Entries may be null if no activity was found for that +     *        specific action. +     * @return The number of menu items that were added. +     * +     * @see #FLAG_APPEND_TO_GROUP +     * @see MenuItem#setIntent +     * @see android.content.pm.PackageManager#queryIntentActivityOptions +     */ +    public int addIntentOptions(int groupId, int itemId, int order, +                                ComponentName caller, Intent[] specifics, +                                Intent intent, int flags, MenuItem[] outSpecificItems); + +    /** +     * Remove the item with the given identifier. +     * +     * @param id The item to be removed.  If there is no item with this +     *           identifier, nothing happens. +     */ +    public void removeItem(int id); + +    /** +     * Remove all items in the given group. +     * +     * @param groupId The group to be removed.  If there are no items in this +     *           group, nothing happens. +     */ +    public void removeGroup(int groupId); + +    /** +     * Remove all existing items from the menu, leaving it empty as if it had +     * just been created. +     */ +    public void clear(); + +    /** +     * Control whether a particular group of items can show a check mark.  This +     * is similar to calling {@link MenuItem#setCheckable} on all of the menu items +     * with the given group identifier, but in addition you can control whether +     * this group contains a mutually-exclusive set items.  This should be called +     * after the items of the group have been added to the menu. +     * +     * @param group The group of items to operate on. +     * @param checkable Set to true to allow a check mark, false to +     *                  disallow.  The default is false. +     * @param exclusive If set to true, only one item in this group can be +     *                  checked at a time; checking an item will automatically +     *                  uncheck all others in the group.  If set to false, each +     *                  item can be checked independently of the others. +     * +     * @see MenuItem#setCheckable +     * @see MenuItem#setChecked +     */ +    public void setGroupCheckable(int group, boolean checkable, boolean exclusive); + +    /** +     * Show or hide all menu items that are in the given group. +     * +     * @param group The group of items to operate on. +     * @param visible If true the items are visible, else they are hidden. +     * +     * @see MenuItem#setVisible +     */ +    public void setGroupVisible(int group, boolean visible); + +    /** +     * Enable or disable all menu items that are in the given group. +     * +     * @param group The group of items to operate on. +     * @param enabled If true the items will be enabled, else they will be disabled. +     * +     * @see MenuItem#setEnabled +     */ +    public void setGroupEnabled(int group, boolean enabled); + +    /** +     * Return whether the menu currently has item items that are visible. +     * +     * @return True if there is one or more item visible, +     *         else false. +     */ +    public boolean hasVisibleItems(); + +    /** +     * Return the menu item with a particular identifier. +     * +     * @param id The identifier to find. +     * +     * @return The menu item object, or null if there is no item with +     *         this identifier. +     */ +    public MenuItem findItem(int id); + +    /** +     * Get the number of items in the menu.  Note that this will change any +     * times items are added or removed from the menu. +     * +     * @return The item count. +     */ +    public int size(); + +    /** +     * Gets the menu item at the given index. +     * +     * @param index The index of the menu item to return. +     * @return The menu item. +     * @exception IndexOutOfBoundsException +     *                when {@code index < 0 || >= size()} +     */ +    public MenuItem getItem(int index); + +    /** +     * Closes the menu, if open. +     */ +    public void close(); + +    /** +     * Execute the menu item action associated with the given shortcut +     * character. +     * +     * @param keyCode The keycode of the shortcut key. +     * @param event Key event message. +     * @param flags Additional option flags or 0. +     * +     * @return If the given shortcut exists and is shown, returns +     *         true; else returns false. +     * +     * @see #FLAG_PERFORM_NO_CLOSE +     */ +    public boolean performShortcut(int keyCode, KeyEvent event, int flags); + +    /** +     * Is a keypress one of the defined shortcut keys for this window. +     * @param keyCode the key code from {@link KeyEvent} to check. +     * @param event the {@link KeyEvent} to use to help check. +     */ +    boolean isShortcutKey(int keyCode, KeyEvent event); + +    /** +     * Execute the menu item action associated with the given menu identifier. +     * +     * @param id Identifier associated with the menu item. +     * @param flags Additional option flags or 0. +     * +     * @return If the given identifier exists and is shown, returns +     *         true; else returns false. +     * +     * @see #FLAG_PERFORM_NO_CLOSE +     */ +    public boolean performIdentifierAction(int id, int flags); + + +    /** +     * Control whether the menu should be running in qwerty mode (alphabetic +     * shortcuts) or 12-key mode (numeric shortcuts). +     * +     * @param isQwerty If true the menu will use alphabetic shortcuts; else it +     *                 will use numeric shortcuts. +     */ +    public void setQwertyMode(boolean isQwerty); +} + diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/MenuInflater.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/MenuInflater.java new file mode 100644 index 000000000..5a0f40859 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/MenuInflater.java @@ -0,0 +1,495 @@ +/*
 + * Copyright (C) 2006 The Android Open Source Project
 + *               2011 Jake Wharton
 + *
 + * 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.actionbarsherlock.view;
 +
 +import java.io.IOException;
 +import java.lang.reflect.Constructor;
 +import java.lang.reflect.Method;
 +import org.xmlpull.v1.XmlPullParser;
 +import org.xmlpull.v1.XmlPullParserException;
 +import android.content.Context;
 +import android.content.res.TypedArray;
 +import android.content.res.XmlResourceParser;
 +import android.util.AttributeSet;
 +import android.util.Log;
 +import android.util.TypedValue;
 +import android.util.Xml;
 +import android.view.InflateException;
 +import android.view.View;
 +
 +import com.actionbarsherlock.R;
 +import com.actionbarsherlock.internal.view.menu.MenuItemImpl;
 +
 +/**
 + * This class is used to instantiate menu XML files into Menu objects.
 + * <p>
 + * For performance reasons, menu inflation relies heavily on pre-processing of
 + * XML files that is done at build time. Therefore, it is not currently possible
 + * to use MenuInflater with an XmlPullParser over a plain XML file at runtime;
 + * it only works with an XmlPullParser returned from a compiled resource (R.
 + * <em>something</em> file.)
 + */
 +public class MenuInflater {
 +    private static final String LOG_TAG = "MenuInflater";
 +
 +    /** Menu tag name in XML. */
 +    private static final String XML_MENU = "menu";
 +
 +    /** Group tag name in XML. */
 +    private static final String XML_GROUP = "group";
 +
 +    /** Item tag name in XML. */
 +    private static final String XML_ITEM = "item";
 +
 +    private static final int NO_ID = 0;
 +
 +    private static final Class<?>[] ACTION_VIEW_CONSTRUCTOR_SIGNATURE = new Class[] {Context.class};
 +
 +    private static final Class<?>[] ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE = ACTION_VIEW_CONSTRUCTOR_SIGNATURE;
 +
 +    private final Object[] mActionViewConstructorArguments;
 +
 +    private final Object[] mActionProviderConstructorArguments;
 +
 +    private Context mContext;
 +    private Object mRealOwner;
 +
 +    /**
 +     * Constructs a menu inflater.
 +     *
 +     * @see Activity#getMenuInflater()
 +     */
 +    public MenuInflater(Context context) {
 +        mContext = context;
 +        mRealOwner = context;
 +        mActionViewConstructorArguments = new Object[] {context};
 +        mActionProviderConstructorArguments = mActionViewConstructorArguments;
 +    }
 +
 +    /**
 +     * Constructs a menu inflater.
 +     *
 +     * @see Activity#getMenuInflater()
 +     * @hide
 +     */
 +    public MenuInflater(Context context, Object realOwner) {
 +        mContext = context;
 +        mRealOwner = realOwner;
 +        mActionViewConstructorArguments = new Object[] {context};
 +        mActionProviderConstructorArguments = mActionViewConstructorArguments;
 +    }
 +
 +    /**
 +     * Inflate a menu hierarchy from the specified XML resource. Throws
 +     * {@link InflateException} if there is an error.
 +     *
 +     * @param menuRes Resource ID for an XML layout resource to load (e.g.,
 +     *            <code>R.menu.main_activity</code>)
 +     * @param menu The Menu to inflate into. The items and submenus will be
 +     *            added to this Menu.
 +     */
 +    public void inflate(int menuRes, Menu menu) {
 +        XmlResourceParser parser = null;
 +        try {
 +            parser = mContext.getResources().getLayout(menuRes);
 +            AttributeSet attrs = Xml.asAttributeSet(parser);
 +
 +            parseMenu(parser, attrs, menu);
 +        } catch (XmlPullParserException e) {
 +            throw new InflateException("Error inflating menu XML", e);
 +        } catch (IOException e) {
 +            throw new InflateException("Error inflating menu XML", e);
 +        } finally {
 +            if (parser != null) parser.close();
 +        }
 +    }
 +
 +    /**
 +     * Called internally to fill the given menu. If a sub menu is seen, it will
 +     * call this recursively.
 +     */
 +    private void parseMenu(XmlPullParser parser, AttributeSet attrs, Menu menu)
 +            throws XmlPullParserException, IOException {
 +        MenuState menuState = new MenuState(menu);
 +
 +        int eventType = parser.getEventType();
 +        String tagName;
 +        boolean lookingForEndOfUnknownTag = false;
 +        String unknownTagName = null;
 +
 +        // This loop will skip to the menu start tag
 +        do {
 +            if (eventType == XmlPullParser.START_TAG) {
 +                tagName = parser.getName();
 +                if (tagName.equals(XML_MENU)) {
 +                    // Go to next tag
 +                    eventType = parser.next();
 +                    break;
 +                }
 +
 +                throw new RuntimeException("Expecting menu, got " + tagName);
 +            }
 +            eventType = parser.next();
 +        } while (eventType != XmlPullParser.END_DOCUMENT);
 +
 +        boolean reachedEndOfMenu = false;
 +        while (!reachedEndOfMenu) {
 +            switch (eventType) {
 +                case XmlPullParser.START_TAG:
 +                    if (lookingForEndOfUnknownTag) {
 +                        break;
 +                    }
 +
 +                    tagName = parser.getName();
 +                    if (tagName.equals(XML_GROUP)) {
 +                        menuState.readGroup(attrs);
 +                    } else if (tagName.equals(XML_ITEM)) {
 +                        menuState.readItem(attrs);
 +                    } else if (tagName.equals(XML_MENU)) {
 +                        // A menu start tag denotes a submenu for an item
 +                        SubMenu subMenu = menuState.addSubMenuItem();
 +
 +                        // Parse the submenu into returned SubMenu
 +                        parseMenu(parser, attrs, subMenu);
 +                    } else {
 +                        lookingForEndOfUnknownTag = true;
 +                        unknownTagName = tagName;
 +                    }
 +                    break;
 +
 +                case XmlPullParser.END_TAG:
 +                    tagName = parser.getName();
 +                    if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) {
 +                        lookingForEndOfUnknownTag = false;
 +                        unknownTagName = null;
 +                    } else if (tagName.equals(XML_GROUP)) {
 +                        menuState.resetGroup();
 +                    } else if (tagName.equals(XML_ITEM)) {
 +                        // Add the item if it hasn't been added (if the item was
 +                        // a submenu, it would have been added already)
 +                        if (!menuState.hasAddedItem()) {
 +                            if (menuState.itemActionProvider != null &&
 +                                    menuState.itemActionProvider.hasSubMenu()) {
 +                                menuState.addSubMenuItem();
 +                            } else {
 +                                menuState.addItem();
 +                            }
 +                        }
 +                    } else if (tagName.equals(XML_MENU)) {
 +                        reachedEndOfMenu = true;
 +                    }
 +                    break;
 +
 +                case XmlPullParser.END_DOCUMENT:
 +                    throw new RuntimeException("Unexpected end of document");
 +            }
 +
 +            eventType = parser.next();
 +        }
 +    }
 +
 +    private static class InflatedOnMenuItemClickListener
 +            implements MenuItem.OnMenuItemClickListener {
 +        private static final Class<?>[] PARAM_TYPES = new Class[] { MenuItem.class };
 +
 +        private Object mRealOwner;
 +        private Method mMethod;
 +
 +        public InflatedOnMenuItemClickListener(Object realOwner, String methodName) {
 +            mRealOwner = realOwner;
 +            Class<?> c = realOwner.getClass();
 +            try {
 +                mMethod = c.getMethod(methodName, PARAM_TYPES);
 +            } catch (Exception e) {
 +                InflateException ex = new InflateException(
 +                        "Couldn't resolve menu item onClick handler " + methodName +
 +                        " in class " + c.getName());
 +                ex.initCause(e);
 +                throw ex;
 +            }
 +        }
 +
 +        public boolean onMenuItemClick(MenuItem item) {
 +            try {
 +                if (mMethod.getReturnType() == Boolean.TYPE) {
 +                    return (Boolean) mMethod.invoke(mRealOwner, item);
 +                } else {
 +                    mMethod.invoke(mRealOwner, item);
 +                    return true;
 +                }
 +            } catch (Exception e) {
 +                throw new RuntimeException(e);
 +            }
 +        }
 +    }
 +
 +    /**
 +     * State for the current menu.
 +     * <p>
 +     * Groups can not be nested unless there is another menu (which will have
 +     * its state class).
 +     */
 +    private class MenuState {
 +        private Menu menu;
 +
 +        /*
 +         * Group state is set on items as they are added, allowing an item to
 +         * override its group state. (As opposed to set on items at the group end tag.)
 +         */
 +        private int groupId;
 +        private int groupCategory;
 +        private int groupOrder;
 +        private int groupCheckable;
 +        private boolean groupVisible;
 +        private boolean groupEnabled;
 +
 +        private boolean itemAdded;
 +        private int itemId;
 +        private int itemCategoryOrder;
 +        private CharSequence itemTitle;
 +        private CharSequence itemTitleCondensed;
 +        private int itemIconResId;
 +        private char itemAlphabeticShortcut;
 +        private char itemNumericShortcut;
 +        /**
 +         * Sync to attrs.xml enum:
 +         * - 0: none
 +         * - 1: all
 +         * - 2: exclusive
 +         */
 +        private int itemCheckable;
 +        private boolean itemChecked;
 +        private boolean itemVisible;
 +        private boolean itemEnabled;
 +
 +        /**
 +         * Sync to attrs.xml enum, values in MenuItem:
 +         * - 0: never
 +         * - 1: ifRoom
 +         * - 2: always
 +         * - -1: Safe sentinel for "no value".
 +         */
 +        private int itemShowAsAction;
 +
 +        private int itemActionViewLayout;
 +        private String itemActionViewClassName;
 +        private String itemActionProviderClassName;
 +
 +        private String itemListenerMethodName;
 +
 +        private ActionProvider itemActionProvider;
 +
 +        private static final int defaultGroupId = NO_ID;
 +        private static final int defaultItemId = NO_ID;
 +        private static final int defaultItemCategory = 0;
 +        private static final int defaultItemOrder = 0;
 +        private static final int defaultItemCheckable = 0;
 +        private static final boolean defaultItemChecked = false;
 +        private static final boolean defaultItemVisible = true;
 +        private static final boolean defaultItemEnabled = true;
 +
 +        public MenuState(final Menu menu) {
 +            this.menu = menu;
 +
 +            resetGroup();
 +        }
 +
 +        public void resetGroup() {
 +            groupId = defaultGroupId;
 +            groupCategory = defaultItemCategory;
 +            groupOrder = defaultItemOrder;
 +            groupCheckable = defaultItemCheckable;
 +            groupVisible = defaultItemVisible;
 +            groupEnabled = defaultItemEnabled;
 +        }
 +
 +        /**
 +         * Called when the parser is pointing to a group tag.
 +         */
 +        public void readGroup(AttributeSet attrs) {
 +            TypedArray a = mContext.obtainStyledAttributes(attrs,
 +                    R.styleable.SherlockMenuGroup);
 +
 +            groupId = a.getResourceId(R.styleable.SherlockMenuGroup_android_id, defaultGroupId);
 +            groupCategory = a.getInt(R.styleable.SherlockMenuGroup_android_menuCategory, defaultItemCategory);
 +            groupOrder = a.getInt(R.styleable.SherlockMenuGroup_android_orderInCategory, defaultItemOrder);
 +            groupCheckable = a.getInt(R.styleable.SherlockMenuGroup_android_checkableBehavior, defaultItemCheckable);
 +            groupVisible = a.getBoolean(R.styleable.SherlockMenuGroup_android_visible, defaultItemVisible);
 +            groupEnabled = a.getBoolean(R.styleable.SherlockMenuGroup_android_enabled, defaultItemEnabled);
 +
 +            a.recycle();
 +        }
 +
 +        /**
 +         * Called when the parser is pointing to an item tag.
 +         */
 +        public void readItem(AttributeSet attrs) {
 +            TypedArray a = mContext.obtainStyledAttributes(attrs,
 +                    R.styleable.SherlockMenuItem);
 +
 +            // Inherit attributes from the group as default value
 +            itemId = a.getResourceId(R.styleable.SherlockMenuItem_android_id, defaultItemId);
 +            final int category = a.getInt(R.styleable.SherlockMenuItem_android_menuCategory, groupCategory);
 +            final int order = a.getInt(R.styleable.SherlockMenuItem_android_orderInCategory, groupOrder);
 +            itemCategoryOrder = (category & Menu.CATEGORY_MASK) | (order & Menu.USER_MASK);
 +            itemTitle = a.getText(R.styleable.SherlockMenuItem_android_title);
 +            itemTitleCondensed = a.getText(R.styleable.SherlockMenuItem_android_titleCondensed);
 +            itemIconResId = a.getResourceId(R.styleable.SherlockMenuItem_android_icon, 0);
 +            itemAlphabeticShortcut =
 +                    getShortcut(a.getString(R.styleable.SherlockMenuItem_android_alphabeticShortcut));
 +            itemNumericShortcut =
 +                    getShortcut(a.getString(R.styleable.SherlockMenuItem_android_numericShortcut));
 +            if (a.hasValue(R.styleable.SherlockMenuItem_android_checkable)) {
 +                // Item has attribute checkable, use it
 +                itemCheckable = a.getBoolean(R.styleable.SherlockMenuItem_android_checkable, false) ? 1 : 0;
 +            } else {
 +                // Item does not have attribute, use the group's (group can have one more state
 +                // for checkable that represents the exclusive checkable)
 +                itemCheckable = groupCheckable;
 +            }
 +
 +            itemChecked = a.getBoolean(R.styleable.SherlockMenuItem_android_checked, defaultItemChecked);
 +            itemVisible = a.getBoolean(R.styleable.SherlockMenuItem_android_visible, groupVisible);
 +            itemEnabled = a.getBoolean(R.styleable.SherlockMenuItem_android_enabled, groupEnabled);
 +
 +            TypedValue value = new TypedValue();
 +            a.getValue(R.styleable.SherlockMenuItem_android_showAsAction, value);
 +            itemShowAsAction = value.type == TypedValue.TYPE_INT_HEX ? value.data : -1;
 +
 +            itemListenerMethodName = a.getString(R.styleable.SherlockMenuItem_android_onClick);
 +            itemActionViewLayout = a.getResourceId(R.styleable.SherlockMenuItem_android_actionLayout, 0);
 +
 +            // itemActionViewClassName = a.getString(R.styleable.SherlockMenuItem_android_actionViewClass);
 +            value = new TypedValue();
 +            a.getValue(R.styleable.SherlockMenuItem_android_actionViewClass, value);
 +            itemActionViewClassName = value.type == TypedValue.TYPE_STRING ? value.string.toString() : null;
 +
 +            // itemActionProviderClassName = a.getString(R.styleable.SherlockMenuItem_android_actionProviderClass);
 +            value = new TypedValue();
 +            a.getValue(R.styleable.SherlockMenuItem_android_actionProviderClass, value);
 +            itemActionProviderClassName = value.type == TypedValue.TYPE_STRING ? value.string.toString() : null;
 +
 +            final boolean hasActionProvider = itemActionProviderClassName != null;
 +            if (hasActionProvider && itemActionViewLayout == 0 && itemActionViewClassName == null) {
 +                itemActionProvider = newInstance(itemActionProviderClassName,
 +                            ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE,
 +                            mActionProviderConstructorArguments);
 +            } else {
 +                if (hasActionProvider) {
 +                    Log.w(LOG_TAG, "Ignoring attribute 'actionProviderClass'."
 +                            + " Action view already specified.");
 +                }
 +                itemActionProvider = null;
 +            }
 +
 +            a.recycle();
 +
 +            itemAdded = false;
 +        }
 +
 +        private char getShortcut(String shortcutString) {
 +            if (shortcutString == null) {
 +                return 0;
 +            } else {
 +                return shortcutString.charAt(0);
 +            }
 +        }
 +
 +        private void setItem(MenuItem item) {
 +            item.setChecked(itemChecked)
 +                .setVisible(itemVisible)
 +                .setEnabled(itemEnabled)
 +                .setCheckable(itemCheckable >= 1)
 +                .setTitleCondensed(itemTitleCondensed)
 +                .setIcon(itemIconResId)
 +                .setAlphabeticShortcut(itemAlphabeticShortcut)
 +                .setNumericShortcut(itemNumericShortcut);
 +
 +            if (itemShowAsAction >= 0) {
 +                item.setShowAsAction(itemShowAsAction);
 +            }
 +
 +            if (itemListenerMethodName != null) {
 +                if (mContext.isRestricted()) {
 +                    throw new IllegalStateException("The android:onClick attribute cannot "
 +                            + "be used within a restricted context");
 +                }
 +                item.setOnMenuItemClickListener(
 +                        new InflatedOnMenuItemClickListener(mRealOwner, itemListenerMethodName));
 +            }
 +
 +            if (itemCheckable >= 2) {
 +                if (item instanceof MenuItemImpl) {
 +                    MenuItemImpl impl = (MenuItemImpl) item;
 +                    impl.setExclusiveCheckable(true);
 +                } else {
 +                    menu.setGroupCheckable(groupId, true, true);
 +                }
 +            }
 +
 +            boolean actionViewSpecified = false;
 +            if (itemActionViewClassName != null) {
 +                View actionView = (View) newInstance(itemActionViewClassName,
 +                        ACTION_VIEW_CONSTRUCTOR_SIGNATURE, mActionViewConstructorArguments);
 +                item.setActionView(actionView);
 +                actionViewSpecified = true;
 +            }
 +            if (itemActionViewLayout > 0) {
 +                if (!actionViewSpecified) {
 +                    item.setActionView(itemActionViewLayout);
 +                    actionViewSpecified = true;
 +                } else {
 +                    Log.w(LOG_TAG, "Ignoring attribute 'itemActionViewLayout'."
 +                            + " Action view already specified.");
 +                }
 +            }
 +            if (itemActionProvider != null) {
 +                item.setActionProvider(itemActionProvider);
 +            }
 +        }
 +
 +        public void addItem() {
 +            itemAdded = true;
 +            setItem(menu.add(groupId, itemId, itemCategoryOrder, itemTitle));
 +        }
 +
 +        public SubMenu addSubMenuItem() {
 +            itemAdded = true;
 +            SubMenu subMenu = menu.addSubMenu(groupId, itemId, itemCategoryOrder, itemTitle);
 +            setItem(subMenu.getItem());
 +            return subMenu;
 +        }
 +
 +        public boolean hasAddedItem() {
 +            return itemAdded;
 +        }
 +
 +        @SuppressWarnings("unchecked")
 +        private <T> T newInstance(String className, Class<?>[] constructorSignature,
 +                Object[] arguments) {
 +            try {
 +                Class<?> clazz = mContext.getClassLoader().loadClass(className);
 +                Constructor<?> constructor = clazz.getConstructor(constructorSignature);
 +                return (T) constructor.newInstance(arguments);
 +            } catch (Exception e) {
 +                Log.w(LOG_TAG, "Cannot instantiate class: " + className, e);
 +            }
 +            return null;
 +        }
 +    }
 +}
 diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/MenuItem.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/MenuItem.java new file mode 100644 index 000000000..7fc3aa430 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/MenuItem.java @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.actionbarsherlock.view; + +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.View; + +/** + * Interface for direct access to a previously created menu item. + * <p> + * An Item is returned by calling one of the {@link android.view.Menu#add} + * methods. + * <p> + * For a feature set of specific menu types, see {@link Menu}. + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For information about creating menus, read the + * <a href="{@docRoot}guide/topics/ui/menus.html">Menus</a> developer guide.</p> + * </div> + */ +public interface MenuItem { +    /* +     * These should be kept in sync with attrs.xml enum constants for showAsAction +     */ +    /** Never show this item as a button in an Action Bar. */ +    public static final int SHOW_AS_ACTION_NEVER = android.view.MenuItem.SHOW_AS_ACTION_NEVER; +    /** Show this item as a button in an Action Bar if the system decides there is room for it. */ +    public static final int SHOW_AS_ACTION_IF_ROOM = android.view.MenuItem.SHOW_AS_ACTION_IF_ROOM; +    /** +     * Always show this item as a button in an Action Bar. +     * Use sparingly! If too many items are set to always show in the Action Bar it can +     * crowd the Action Bar and degrade the user experience on devices with smaller screens. +     * A good rule of thumb is to have no more than 2 items set to always show at a time. +     */ +    public static final int SHOW_AS_ACTION_ALWAYS = android.view.MenuItem.SHOW_AS_ACTION_ALWAYS; + +    /** +     * When this item is in the action bar, always show it with a text label even if +     * it also has an icon specified. +     */ +    public static final int SHOW_AS_ACTION_WITH_TEXT = android.view.MenuItem.SHOW_AS_ACTION_WITH_TEXT; + +    /** +     * This item's action view collapses to a normal menu item. +     * When expanded, the action view temporarily takes over +     * a larger segment of its container. +     */ +    public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = android.view.MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW; + +    /** +     * Interface definition for a callback to be invoked when a menu item is +     * clicked. +     * +     * @see Activity#onContextItemSelected(MenuItem) +     * @see Activity#onOptionsItemSelected(MenuItem) +     */ +    public interface OnMenuItemClickListener { +        /** +         * Called when a menu item has been invoked.  This is the first code +         * that is executed; if it returns true, no other callbacks will be +         * executed. +         * +         * @param item The menu item that was invoked. +         * +         * @return Return true to consume this click and prevent others from +         *         executing. +         */ +        public boolean onMenuItemClick(MenuItem item); +    } + +    /** +     * Interface definition for a callback to be invoked when a menu item +     * marked with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} is +     * expanded or collapsed. +     * +     * @see MenuItem#expandActionView() +     * @see MenuItem#collapseActionView() +     * @see MenuItem#setShowAsActionFlags(int) +     */ +    public interface OnActionExpandListener { +        /** +         * Called when a menu item with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} +         * is expanded. +         * @param item Item that was expanded +         * @return true if the item should expand, false if expansion should be suppressed. +         */ +        public boolean onMenuItemActionExpand(MenuItem item); + +        /** +         * Called when a menu item with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} +         * is collapsed. +         * @param item Item that was collapsed +         * @return true if the item should collapse, false if collapsing should be suppressed. +         */ +        public boolean onMenuItemActionCollapse(MenuItem item); +    } + +    /** +     * Return the identifier for this menu item.  The identifier can not +     * be changed after the menu is created. +     * +     * @return The menu item's identifier. +     */ +    public int getItemId(); + +    /** +     * Return the group identifier that this menu item is part of. The group +     * identifier can not be changed after the menu is created. +     * +     * @return The menu item's group identifier. +     */ +    public int getGroupId(); + +    /** +     * Return the category and order within the category of this item. This +     * item will be shown before all items (within its category) that have +     * order greater than this value. +     * <p> +     * An order integer contains the item's category (the upper bits of the +     * integer; set by or/add the category with the order within the +     * category) and the ordering of the item within that category (the +     * lower bits). Example categories are {@link Menu#CATEGORY_SYSTEM}, +     * {@link Menu#CATEGORY_SECONDARY}, {@link Menu#CATEGORY_ALTERNATIVE}, +     * {@link Menu#CATEGORY_CONTAINER}. See {@link Menu} for a full list. +     * +     * @return The order of this item. +     */ +    public int getOrder(); + +    /** +     * Change the title associated with this item. +     * +     * @param title The new text to be displayed. +     * @return This Item so additional setters can be called. +     */ +    public MenuItem setTitle(CharSequence title); + +    /** +     * Change the title associated with this item. +     * <p> +     * Some menu types do not sufficient space to show the full title, and +     * instead a condensed title is preferred. See {@link Menu} for more +     * information. +     * +     * @param title The resource id of the new text to be displayed. +     * @return This Item so additional setters can be called. +     * @see #setTitleCondensed(CharSequence) +     */ + +    public MenuItem setTitle(int title); + +    /** +     * Retrieve the current title of the item. +     * +     * @return The title. +     */ +    public CharSequence getTitle(); + +    /** +     * Change the condensed title associated with this item. The condensed +     * title is used in situations where the normal title may be too long to +     * be displayed. +     * +     * @param title The new text to be displayed as the condensed title. +     * @return This Item so additional setters can be called. +     */ +    public MenuItem setTitleCondensed(CharSequence title); + +    /** +     * Retrieve the current condensed title of the item. If a condensed +     * title was never set, it will return the normal title. +     * +     * @return The condensed title, if it exists. +     *         Otherwise the normal title. +     */ +    public CharSequence getTitleCondensed(); + +    /** +     * Change the icon associated with this item. This icon will not always be +     * shown, so the title should be sufficient in describing this item. See +     * {@link Menu} for the menu types that support icons. +     * +     * @param icon The new icon (as a Drawable) to be displayed. +     * @return This Item so additional setters can be called. +     */ +    public MenuItem setIcon(Drawable icon); + +    /** +     * Change the icon associated with this item. This icon will not always be +     * shown, so the title should be sufficient in describing this item. See +     * {@link Menu} for the menu types that support icons. +     * <p> +     * This method will set the resource ID of the icon which will be used to +     * lazily get the Drawable when this item is being shown. +     * +     * @param iconRes The new icon (as a resource ID) to be displayed. +     * @return This Item so additional setters can be called. +     */ +    public MenuItem setIcon(int iconRes); + +    /** +     * Returns the icon for this item as a Drawable (getting it from resources if it hasn't been +     * loaded before). +     * +     * @return The icon as a Drawable. +     */ +    public Drawable getIcon(); + +    /** +     * Change the Intent associated with this item.  By default there is no +     * Intent associated with a menu item.  If you set one, and nothing +     * else handles the item, then the default behavior will be to call +     * {@link android.content.Context#startActivity} with the given Intent. +     * +     * <p>Note that setIntent() can not be used with the versions of +     * {@link Menu#add} that take a Runnable, because {@link Runnable#run} +     * does not return a value so there is no way to tell if it handled the +     * item.  In this case it is assumed that the Runnable always handles +     * the item, and the intent will never be started. +     * +     * @see #getIntent +     * @param intent The Intent to associated with the item.  This Intent +     *               object is <em>not</em> copied, so be careful not to +     *               modify it later. +     * @return This Item so additional setters can be called. +     */ +    public MenuItem setIntent(Intent intent); + +    /** +     * Return the Intent associated with this item.  This returns a +     * reference to the Intent which you can change as desired to modify +     * what the Item is holding. +     * +     * @see #setIntent +     * @return Returns the last value supplied to {@link #setIntent}, or +     *         null. +     */ +    public Intent getIntent(); + +    /** +     * Change both the numeric and alphabetic shortcut associated with this +     * item. Note that the shortcut will be triggered when the key that +     * generates the given character is pressed alone or along with with the alt +     * key. Also note that case is not significant and that alphabetic shortcut +     * characters will be displayed in lower case. +     * <p> +     * See {@link Menu} for the menu types that support shortcuts. +     * +     * @param numericChar The numeric shortcut key. This is the shortcut when +     *        using a numeric (e.g., 12-key) keyboard. +     * @param alphaChar The alphabetic shortcut key. This is the shortcut when +     *        using a keyboard with alphabetic keys. +     * @return This Item so additional setters can be called. +     */ +    public MenuItem setShortcut(char numericChar, char alphaChar); + +    /** +     * Change the numeric shortcut associated with this item. +     * <p> +     * See {@link Menu} for the menu types that support shortcuts. +     * +     * @param numericChar The numeric shortcut key.  This is the shortcut when +     *                 using a 12-key (numeric) keyboard. +     * @return This Item so additional setters can be called. +     */ +    public MenuItem setNumericShortcut(char numericChar); + +    /** +     * Return the char for this menu item's numeric (12-key) shortcut. +     * +     * @return Numeric character to use as a shortcut. +     */ +    public char getNumericShortcut(); + +    /** +     * Change the alphabetic shortcut associated with this item. The shortcut +     * will be triggered when the key that generates the given character is +     * pressed alone or along with with the alt key. Case is not significant and +     * shortcut characters will be displayed in lower case. Note that menu items +     * with the characters '\b' or '\n' as shortcuts will get triggered by the +     * Delete key or Carriage Return key, respectively. +     * <p> +     * See {@link Menu} for the menu types that support shortcuts. +     * +     * @param alphaChar The alphabetic shortcut key. This is the shortcut when +     *        using a keyboard with alphabetic keys. +     * @return This Item so additional setters can be called. +     */ +    public MenuItem setAlphabeticShortcut(char alphaChar); + +    /** +     * Return the char for this menu item's alphabetic shortcut. +     * +     * @return Alphabetic character to use as a shortcut. +     */ +    public char getAlphabeticShortcut(); + +    /** +     * Control whether this item can display a check mark. Setting this does +     * not actually display a check mark (see {@link #setChecked} for that); +     * rather, it ensures there is room in the item in which to display a +     * check mark. +     * <p> +     * See {@link Menu} for the menu types that support check marks. +     * +     * @param checkable Set to true to allow a check mark, false to +     *            disallow. The default is false. +     * @see #setChecked +     * @see #isCheckable +     * @see Menu#setGroupCheckable +     * @return This Item so additional setters can be called. +     */ +    public MenuItem setCheckable(boolean checkable); + +    /** +     * Return whether the item can currently display a check mark. +     * +     * @return If a check mark can be displayed, returns true. +     * +     * @see #setCheckable +     */ +    public boolean isCheckable(); + +    /** +     * Control whether this item is shown with a check mark.  Note that you +     * must first have enabled checking with {@link #setCheckable} or else +     * the check mark will not appear.  If this item is a member of a group that contains +     * mutually-exclusive items (set via {@link Menu#setGroupCheckable(int, boolean, boolean)}, +     * the other items in the group will be unchecked. +     * <p> +     * See {@link Menu} for the menu types that support check marks. +     * +     * @see #setCheckable +     * @see #isChecked +     * @see Menu#setGroupCheckable +     * @param checked Set to true to display a check mark, false to hide +     *                it.  The default value is false. +     * @return This Item so additional setters can be called. +     */ +    public MenuItem setChecked(boolean checked); + +    /** +     * Return whether the item is currently displaying a check mark. +     * +     * @return If a check mark is displayed, returns true. +     * +     * @see #setChecked +     */ +    public boolean isChecked(); + +    /** +     * Sets the visibility of the menu item. Even if a menu item is not visible, +     * it may still be invoked via its shortcut (to completely disable an item, +     * set it to invisible and {@link #setEnabled(boolean) disabled}). +     * +     * @param visible If true then the item will be visible; if false it is +     *        hidden. +     * @return This Item so additional setters can be called. +     */ +    public MenuItem setVisible(boolean visible); + +    /** +     * Return the visibility of the menu item. +     * +     * @return If true the item is visible; else it is hidden. +     */ +    public boolean isVisible(); + +    /** +     * Sets whether the menu item is enabled. Disabling a menu item will not +     * allow it to be invoked via its shortcut. The menu item will still be +     * visible. +     * +     * @param enabled If true then the item will be invokable; if false it is +     *        won't be invokable. +     * @return This Item so additional setters can be called. +     */ +    public MenuItem setEnabled(boolean enabled); + +    /** +     * Return the enabled state of the menu item. +     * +     * @return If true the item is enabled and hence invokable; else it is not. +     */ +    public boolean isEnabled(); + +    /** +     * Check whether this item has an associated sub-menu.  I.e. it is a +     * sub-menu of another menu. +     * +     * @return If true this item has a menu; else it is a +     *         normal item. +     */ +    public boolean hasSubMenu(); + +    /** +     * Get the sub-menu to be invoked when this item is selected, if it has +     * one. See {@link #hasSubMenu()}. +     * +     * @return The associated menu if there is one, else null +     */ +    public SubMenu getSubMenu(); + +    /** +     * Set a custom listener for invocation of this menu item. In most +     * situations, it is more efficient and easier to use +     * {@link Activity#onOptionsItemSelected(MenuItem)} or +     * {@link Activity#onContextItemSelected(MenuItem)}. +     * +     * @param menuItemClickListener The object to receive invokations. +     * @return This Item so additional setters can be called. +     * @see Activity#onOptionsItemSelected(MenuItem) +     * @see Activity#onContextItemSelected(MenuItem) +     */ +    public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener menuItemClickListener); + +    /** +     * Gets the extra information linked to this menu item.  This extra +     * information is set by the View that added this menu item to the +     * menu. +     * +     * @see OnCreateContextMenuListener +     * @return The extra information linked to the View that added this +     *         menu item to the menu. This can be null. +     */ +    public ContextMenuInfo getMenuInfo(); + +    /** +     * Sets how this item should display in the presence of an Action Bar. +     * The parameter actionEnum is a flag set. One of {@link #SHOW_AS_ACTION_ALWAYS}, +     * {@link #SHOW_AS_ACTION_IF_ROOM}, or {@link #SHOW_AS_ACTION_NEVER} should +     * be used, and you may optionally OR the value with {@link #SHOW_AS_ACTION_WITH_TEXT}. +     * SHOW_AS_ACTION_WITH_TEXT requests that when the item is shown as an action, +     * it should be shown with a text label. +     * +     * @param actionEnum How the item should display. One of +     * {@link #SHOW_AS_ACTION_ALWAYS}, {@link #SHOW_AS_ACTION_IF_ROOM}, or +     * {@link #SHOW_AS_ACTION_NEVER}. SHOW_AS_ACTION_NEVER is the default. +     * +     * @see android.app.ActionBar +     * @see #setActionView(View) +     */ +    public void setShowAsAction(int actionEnum); + +    /** +     * Sets how this item should display in the presence of an Action Bar. +     * The parameter actionEnum is a flag set. One of {@link #SHOW_AS_ACTION_ALWAYS}, +     * {@link #SHOW_AS_ACTION_IF_ROOM}, or {@link #SHOW_AS_ACTION_NEVER} should +     * be used, and you may optionally OR the value with {@link #SHOW_AS_ACTION_WITH_TEXT}. +     * SHOW_AS_ACTION_WITH_TEXT requests that when the item is shown as an action, +     * it should be shown with a text label. +     * +     * <p>Note: This method differs from {@link #setShowAsAction(int)} only in that it +     * returns the current MenuItem instance for call chaining. +     * +     * @param actionEnum How the item should display. One of +     * {@link #SHOW_AS_ACTION_ALWAYS}, {@link #SHOW_AS_ACTION_IF_ROOM}, or +     * {@link #SHOW_AS_ACTION_NEVER}. SHOW_AS_ACTION_NEVER is the default. +     * +     * @see android.app.ActionBar +     * @see #setActionView(View) +     * @return This MenuItem instance for call chaining. +     */ +    public MenuItem setShowAsActionFlags(int actionEnum); + +    /** +     * Set an action view for this menu item. An action view will be displayed in place +     * of an automatically generated menu item element in the UI when this item is shown +     * as an action within a parent. +     * <p> +     *   <strong>Note:</strong> Setting an action view overrides the action provider +     *           set via {@link #setActionProvider(ActionProvider)}. +     * </p> +     * +     * @param view View to use for presenting this item to the user. +     * @return This Item so additional setters can be called. +     * +     * @see #setShowAsAction(int) +     */ +    public MenuItem setActionView(View view); + +    /** +     * Set an action view for this menu item. An action view will be displayed in place +     * of an automatically generated menu item element in the UI when this item is shown +     * as an action within a parent. +     * <p> +     *   <strong>Note:</strong> Setting an action view overrides the action provider +     *           set via {@link #setActionProvider(ActionProvider)}. +     * </p> +     * +     * @param resId Layout resource to use for presenting this item to the user. +     * @return This Item so additional setters can be called. +     * +     * @see #setShowAsAction(int) +     */ +    public MenuItem setActionView(int resId); + +    /** +     * Returns the currently set action view for this menu item. +     * +     * @return This item's action view +     * +     * @see #setActionView(View) +     * @see #setShowAsAction(int) +     */ +    public View getActionView(); + +    /** +     * Sets the {@link ActionProvider} responsible for creating an action view if +     * the item is placed on the action bar. The provider also provides a default +     * action invoked if the item is placed in the overflow menu. +     * <p> +     *   <strong>Note:</strong> Setting an action provider overrides the action view +     *           set via {@link #setActionView(int)} or {@link #setActionView(View)}. +     * </p> +     * +     * @param actionProvider The action provider. +     * @return This Item so additional setters can be called. +     * +     * @see ActionProvider +     */ +    public MenuItem setActionProvider(ActionProvider actionProvider); + +    /** +     * Gets the {@link ActionProvider}. +     * +     * @return The action provider. +     * +     * @see ActionProvider +     * @see #setActionProvider(ActionProvider) +     */ +    public ActionProvider getActionProvider(); + +    /** +     * Expand the action view associated with this menu item. +     * The menu item must have an action view set, as well as +     * the showAsAction flag {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. +     * If a listener has been set using {@link #setOnActionExpandListener(OnActionExpandListener)} +     * it will have its {@link OnActionExpandListener#onMenuItemActionExpand(MenuItem)} +     * method invoked. The listener may return false from this method to prevent expanding +     * the action view. +     * +     * @return true if the action view was expanded, false otherwise. +     */ +    public boolean expandActionView(); + +    /** +     * Collapse the action view associated with this menu item. +     * The menu item must have an action view set, as well as the showAsAction flag +     * {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. If a listener has been set using +     * {@link #setOnActionExpandListener(OnActionExpandListener)} it will have its +     * {@link OnActionExpandListener#onMenuItemActionCollapse(MenuItem)} method invoked. +     * The listener may return false from this method to prevent collapsing the action view. +     * +     * @return true if the action view was collapsed, false otherwise. +     */ +    public boolean collapseActionView(); + +    /** +     * Returns true if this menu item's action view has been expanded. +     * +     * @return true if the item's action view is expanded, false otherwise. +     * +     * @see #expandActionView() +     * @see #collapseActionView() +     * @see #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW +     * @see OnActionExpandListener +     */ +    public boolean isActionViewExpanded(); + +    /** +     * Set an {@link OnActionExpandListener} on this menu item to be notified when +     * the associated action view is expanded or collapsed. The menu item must +     * be configured to expand or collapse its action view using the flag +     * {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. +     * +     * @param listener Listener that will respond to expand/collapse events +     * @return This menu item instance for call chaining +     */ +    public MenuItem setOnActionExpandListener(OnActionExpandListener listener); +}
\ No newline at end of file diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/SubMenu.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/SubMenu.java new file mode 100644 index 000000000..397fd1c2d --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/SubMenu.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2007 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.actionbarsherlock.view; + +import android.graphics.drawable.Drawable; +import android.view.View; + +/** + * Subclass of {@link Menu} for sub menus. + * <p> + * Sub menus do not support item icons, or nested sub menus. + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For information about creating menus, read the + * <a href="{@docRoot}guide/topics/ui/menus.html">Menus</a> developer guide.</p> + * </div> + */ + +public interface SubMenu extends Menu { +    /** +     * Sets the submenu header's title to the title given in <var>titleRes</var> +     * resource identifier. +     * +     * @param titleRes The string resource identifier used for the title. +     * @return This SubMenu so additional setters can be called. +     */ +    public SubMenu setHeaderTitle(int titleRes); + +    /** +     * Sets the submenu header's title to the title given in <var>title</var>. +     * +     * @param title The character sequence used for the title. +     * @return This SubMenu so additional setters can be called. +     */ +    public SubMenu setHeaderTitle(CharSequence title); + +    /** +     * Sets the submenu header's icon to the icon given in <var>iconRes</var> +     * resource id. +     * +     * @param iconRes The resource identifier used for the icon. +     * @return This SubMenu so additional setters can be called. +     */ +    public SubMenu setHeaderIcon(int iconRes); + +    /** +     * Sets the submenu header's icon to the icon given in <var>icon</var> +     * {@link Drawable}. +     * +     * @param icon The {@link Drawable} used for the icon. +     * @return This SubMenu so additional setters can be called. +     */ +    public SubMenu setHeaderIcon(Drawable icon); + +    /** +     * Sets the header of the submenu to the {@link View} given in +     * <var>view</var>. This replaces the header title and icon (and those +     * replace this). +     * +     * @param view The {@link View} used for the header. +     * @return This SubMenu so additional setters can be called. +     */ +    public SubMenu setHeaderView(View view); + +    /** +     * Clears the header of the submenu. +     */ +    public void clearHeader(); + +    /** +     * Change the icon associated with this submenu's item in its parent menu. +     * +     * @see MenuItem#setIcon(int) +     * @param iconRes The new icon (as a resource ID) to be displayed. +     * @return This SubMenu so additional setters can be called. +     */ +    public SubMenu setIcon(int iconRes); + +    /** +     * Change the icon associated with this submenu's item in its parent menu. +     * +     * @see MenuItem#setIcon(Drawable) +     * @param icon The new icon (as a Drawable) to be displayed. +     * @return This SubMenu so additional setters can be called. +     */ +    public SubMenu setIcon(Drawable icon); + +    /** +     * Gets the {@link MenuItem} that represents this submenu in the parent +     * menu.  Use this for setting additional item attributes. +     * +     * @return The {@link MenuItem} that launches the submenu when invoked. +     */ +    public MenuItem getItem(); +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/Window.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/Window.java new file mode 100644 index 000000000..a340a4291 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/view/Window.java @@ -0,0 +1,65 @@ +/*
 + * Copyright (C) 2006 The Android Open Source Project
 + * Copyright (C) 2011 Jake Wharton
 + *
 + * 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.actionbarsherlock.view;
 +
 +import android.content.Context;
 +
 +/**
 + * <p>Abstract base class for a top-level window look and behavior policy. An
 + * instance of this class should be used as the top-level view added to the
 + * window manager. It provides standard UI policies such as a background, title
 + * area, default key processing, etc.</p>
 + *
 + * <p>The only existing implementation of this abstract class is
 + * android.policy.PhoneWindow, which you should instantiate when needing a
 + * Window. Eventually that class will be refactored and a factory method added
 + * for creating Window instances without knowing about a particular
 + * implementation.</p>
 + */
 +public abstract class Window extends android.view.Window {
 +    public static final long FEATURE_ACTION_BAR = android.view.Window.FEATURE_ACTION_BAR;
 +    public static final long FEATURE_ACTION_BAR_OVERLAY = android.view.Window.FEATURE_ACTION_BAR_OVERLAY;
 +    public static final long FEATURE_ACTION_MODE_OVERLAY = android.view.Window.FEATURE_ACTION_MODE_OVERLAY;
 +    public static final long FEATURE_NO_TITLE = android.view.Window.FEATURE_NO_TITLE;
 +    public static final long FEATURE_PROGRESS = android.view.Window.FEATURE_PROGRESS;
 +    public static final long FEATURE_INDETERMINATE_PROGRESS = android.view.Window.FEATURE_INDETERMINATE_PROGRESS;
 +
 +    /**
 +     * Create a new instance for a context.
 +     *
 +     * @param context Context.
 +     */
 +    private Window(Context context) {
 +        super(context);
 +    }
 +
 +
 +    public interface Callback {
 +        /**
 +         * Called when a panel's menu item has been selected by the user.
 +         *
 +         * @param featureId The panel that the menu is in.
 +         * @param item The menu item that was selected.
 +         *
 +         * @return boolean Return true to finish processing of selection, or
 +         *         false to perform the normal menu handling (calling its
 +         *         Runnable or sending a Message to its target Handler).
 +         */
 +        public boolean onMenuItemSelected(int featureId, MenuItem item);
 +    }
 +}
 diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserModel.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserModel.java new file mode 100644 index 000000000..d7f110fc6 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserModel.java @@ -0,0 +1,1104 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.actionbarsherlock.widget; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.database.DataSetObservable; +import android.os.Handler; +import android.text.TextUtils; +import android.util.Log; +import android.util.Xml; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +/** + * <p> + * This class represents a data model for choosing a component for handing a + * given {@link Intent}. The model is responsible for querying the system for + * activities that can handle the given intent and order found activities + * based on historical data of previous choices. The historical data is stored + * in an application private file. If a client does not want to have persistent + * choice history the file can be omitted, thus the activities will be ordered + * based on historical usage for the current session. + * <p> + * </p> + * For each backing history file there is a singleton instance of this class. Thus, + * several clients that specify the same history file will share the same model. Note + * that if multiple clients are sharing the same model they should implement semantically + * equivalent functionality since setting the model intent will change the found + * activities and they may be inconsistent with the functionality of some of the clients. + * For example, choosing a share activity can be implemented by a single backing + * model and two different views for performing the selection. If however, one of the + * views is used for sharing but the other for importing, for example, then each + * view should be backed by a separate model. + * </p> + * <p> + * The way clients interact with this class is as follows: + * </p> + * <p> + * <pre> + * <code> + *  // Get a model and set it to a couple of clients with semantically similar function. + *  ActivityChooserModel dataModel = + *      ActivityChooserModel.get(context, "task_specific_history_file_name.xml"); + * + *  ActivityChooserModelClient modelClient1 = getActivityChooserModelClient1(); + *  modelClient1.setActivityChooserModel(dataModel); + * + *  ActivityChooserModelClient modelClient2 = getActivityChooserModelClient2(); + *  modelClient2.setActivityChooserModel(dataModel); + * + *  // Set an intent to choose a an activity for. + *  dataModel.setIntent(intent); + * <pre> + * <code> + * </p> + * <p> + * <strong>Note:</strong> This class is thread safe. + * </p> + * + * @hide + */ +class ActivityChooserModel extends DataSetObservable { + +    /** +     * Client that utilizes an {@link ActivityChooserModel}. +     */ +    public interface ActivityChooserModelClient { + +        /** +         * Sets the {@link ActivityChooserModel}. +         * +         * @param dataModel The model. +         */ +        public void setActivityChooserModel(ActivityChooserModel dataModel); +    } + +    /** +     * Defines a sorter that is responsible for sorting the activities +     * based on the provided historical choices and an intent. +     */ +    public interface ActivitySorter { + +        /** +         * Sorts the <code>activities</code> in descending order of relevance +         * based on previous history and an intent. +         * +         * @param intent The {@link Intent}. +         * @param activities Activities to be sorted. +         * @param historicalRecords Historical records. +         */ +        // This cannot be done by a simple comparator since an Activity weight +        // is computed from history. Note that Activity implements Comparable. +        public void sort(Intent intent, List<ActivityResolveInfo> activities, +                List<HistoricalRecord> historicalRecords); +    } + +    /** +     * Listener for choosing an activity. +     */ +    public interface OnChooseActivityListener { + +        /** +         * Called when an activity has been chosen. The client can decide whether +         * an activity can be chosen and if so the caller of +         * {@link ActivityChooserModel#chooseActivity(int)} will receive and {@link Intent} +         * for launching it. +         * <p> +         * <strong>Note:</strong> Modifying the intent is not permitted and +         *     any changes to the latter will be ignored. +         * </p> +         * +         * @param host The listener's host model. +         * @param intent The intent for launching the chosen activity. +         * @return Whether the intent is handled and should not be delivered to clients. +         * +         * @see ActivityChooserModel#chooseActivity(int) +         */ +        public boolean onChooseActivity(ActivityChooserModel host, Intent intent); +    } + +    /** +     * Flag for selecting debug mode. +     */ +    private static final boolean DEBUG = false; + +    /** +     * Tag used for logging. +     */ +    private static final String LOG_TAG = ActivityChooserModel.class.getSimpleName(); + +    /** +     * The root tag in the history file. +     */ +    private static final String TAG_HISTORICAL_RECORDS = "historical-records"; + +    /** +     * The tag for a record in the history file. +     */ +    private static final String TAG_HISTORICAL_RECORD = "historical-record"; + +    /** +     * Attribute for the activity. +     */ +    private static final String ATTRIBUTE_ACTIVITY = "activity"; + +    /** +     * Attribute for the choice time. +     */ +    private static final String ATTRIBUTE_TIME = "time"; + +    /** +     * Attribute for the choice weight. +     */ +    private static final String ATTRIBUTE_WEIGHT = "weight"; + +    /** +     * The default name of the choice history file. +     */ +    public static final String DEFAULT_HISTORY_FILE_NAME = +        "activity_choser_model_history.xml"; + +    /** +     * The default maximal length of the choice history. +     */ +    public static final int DEFAULT_HISTORY_MAX_LENGTH = 50; + +    /** +     * The amount with which to inflate a chosen activity when set as default. +     */ +    private static final int DEFAULT_ACTIVITY_INFLATION = 5; + +    /** +     * Default weight for a choice record. +     */ +    private static final float DEFAULT_HISTORICAL_RECORD_WEIGHT = 1.0f; + +    /** +     * The extension of the history file. +     */ +    private static final String HISTORY_FILE_EXTENSION = ".xml"; + +    /** +     * An invalid item index. +     */ +    private static final int INVALID_INDEX = -1; + +    /** +     * Lock to guard the model registry. +     */ +    private static final Object sRegistryLock = new Object(); + +    /** +     * This the registry for data models. +     */ +    private static final Map<String, ActivityChooserModel> sDataModelRegistry = +        new HashMap<String, ActivityChooserModel>(); + +    /** +     * Lock for synchronizing on this instance. +     */ +    private final Object mInstanceLock = new Object(); + +    /** +     * List of activities that can handle the current intent. +     */ +    private final List<ActivityResolveInfo> mActivites = new ArrayList<ActivityResolveInfo>(); + +    /** +     * List with historical choice records. +     */ +    private final List<HistoricalRecord> mHistoricalRecords = new ArrayList<HistoricalRecord>(); + +    /** +     * Context for accessing resources. +     */ +    private final Context mContext; + +    /** +     * The name of the history file that backs this model. +     */ +    private final String mHistoryFileName; + +    /** +     * The intent for which a activity is being chosen. +     */ +    private Intent mIntent; + +    /** +     * The sorter for ordering activities based on intent and past choices. +     */ +    private ActivitySorter mActivitySorter = new DefaultSorter(); + +    /** +     * The maximal length of the choice history. +     */ +    private int mHistoryMaxSize = DEFAULT_HISTORY_MAX_LENGTH; + +    /** +     * Flag whether choice history can be read. In general many clients can +     * share the same data model and {@link #readHistoricalData()} may be called +     * by arbitrary of them any number of times. Therefore, this class guarantees +     * that the very first read succeeds and subsequent reads can be performed +     * only after a call to {@link #persistHistoricalData()} followed by change +     * of the share records. +     */ +    private boolean mCanReadHistoricalData = true; + +    /** +     * Flag whether the choice history was read. This is used to enforce that +     * before calling {@link #persistHistoricalData()} a call to +     * {@link #persistHistoricalData()} has been made. This aims to avoid a +     * scenario in which a choice history file exits, it is not read yet and +     * it is overwritten. Note that always all historical records are read in +     * full and the file is rewritten. This is necessary since we need to +     * purge old records that are outside of the sliding window of past choices. +     */ +    private boolean mReadShareHistoryCalled = false; + +    /** +     * Flag whether the choice records have changed. In general many clients can +     * share the same data model and {@link #persistHistoricalData()} may be called +     * by arbitrary of them any number of times. Therefore, this class guarantees +     * that choice history will be persisted only if it has changed. +     */ +    private boolean mHistoricalRecordsChanged = true; + +    /** +     * Hander for scheduling work on client tread. +     */ +    private final Handler mHandler = new Handler(); + +    /** +     * Policy for controlling how the model handles chosen activities. +     */ +    private OnChooseActivityListener mActivityChoserModelPolicy; + +    /** +     * Gets the data model backed by the contents of the provided file with historical data. +     * Note that only one data model is backed by a given file, thus multiple calls with +     * the same file name will return the same model instance. If no such instance is present +     * it is created. +     * <p> +     * <strong>Note:</strong> To use the default historical data file clients should explicitly +     * pass as file name {@link #DEFAULT_HISTORY_FILE_NAME}. If no persistence of the choice +     * history is desired clients should pass <code>null</code> for the file name. In such +     * case a new model is returned for each invocation. +     * </p> +     * +     * <p> +     * <strong>Always use difference historical data files for semantically different actions. +     * For example, sharing is different from importing.</strong> +     * </p> +     * +     * @param context Context for loading resources. +     * @param historyFileName File name with choice history, <code>null</code> +     *        if the model should not be backed by a file. In this case the activities +     *        will be ordered only by data from the current session. +     * +     * @return The model. +     */ +    public static ActivityChooserModel get(Context context, String historyFileName) { +        synchronized (sRegistryLock) { +            ActivityChooserModel dataModel = sDataModelRegistry.get(historyFileName); +            if (dataModel == null) { +                dataModel = new ActivityChooserModel(context, historyFileName); +                sDataModelRegistry.put(historyFileName, dataModel); +            } +            dataModel.readHistoricalData(); +            return dataModel; +        } +    } + +    /** +     * Creates a new instance. +     * +     * @param context Context for loading resources. +     * @param historyFileName The history XML file. +     */ +    private ActivityChooserModel(Context context, String historyFileName) { +        mContext = context.getApplicationContext(); +        if (!TextUtils.isEmpty(historyFileName) +                && !historyFileName.endsWith(HISTORY_FILE_EXTENSION)) { +            mHistoryFileName = historyFileName + HISTORY_FILE_EXTENSION; +        } else { +            mHistoryFileName = historyFileName; +        } +    } + +    /** +     * Sets an intent for which to choose a activity. +     * <p> +     * <strong>Note:</strong> Clients must set only semantically similar +     * intents for each data model. +     * <p> +     * +     * @param intent The intent. +     */ +    public void setIntent(Intent intent) { +        synchronized (mInstanceLock) { +            if (mIntent == intent) { +                return; +            } +            mIntent = intent; +            loadActivitiesLocked(); +        } +    } + +    /** +     * Gets the intent for which a activity is being chosen. +     * +     * @return The intent. +     */ +    public Intent getIntent() { +        synchronized (mInstanceLock) { +            return mIntent; +        } +    } + +    /** +     * Gets the number of activities that can handle the intent. +     * +     * @return The activity count. +     * +     * @see #setIntent(Intent) +     */ +    public int getActivityCount() { +        synchronized (mInstanceLock) { +            return mActivites.size(); +        } +    } + +    /** +     * Gets an activity at a given index. +     * +     * @return The activity. +     * +     * @see ActivityResolveInfo +     * @see #setIntent(Intent) +     */ +    public ResolveInfo getActivity(int index) { +        synchronized (mInstanceLock) { +            return mActivites.get(index).resolveInfo; +        } +    } + +    /** +     * Gets the index of a the given activity. +     * +     * @param activity The activity index. +     * +     * @return The index if found, -1 otherwise. +     */ +    public int getActivityIndex(ResolveInfo activity) { +        List<ActivityResolveInfo> activities = mActivites; +        final int activityCount = activities.size(); +        for (int i = 0; i < activityCount; i++) { +            ActivityResolveInfo currentActivity = activities.get(i); +            if (currentActivity.resolveInfo == activity) { +                return i; +            } +        } +        return INVALID_INDEX; +    } + +    /** +     * Chooses a activity to handle the current intent. This will result in +     * adding a historical record for that action and construct intent with +     * its component name set such that it can be immediately started by the +     * client. +     * <p> +     * <strong>Note:</strong> By calling this method the client guarantees +     * that the returned intent will be started. This intent is returned to +     * the client solely to let additional customization before the start. +     * </p> +     * +     * @return An {@link Intent} for launching the activity or null if the +     *         policy has consumed the intent. +     * +     * @see HistoricalRecord +     * @see OnChooseActivityListener +     */ +    public Intent chooseActivity(int index) { +        ActivityResolveInfo chosenActivity = mActivites.get(index); + +        ComponentName chosenName = new ComponentName( +                chosenActivity.resolveInfo.activityInfo.packageName, +                chosenActivity.resolveInfo.activityInfo.name); + +        Intent choiceIntent = new Intent(mIntent); +        choiceIntent.setComponent(chosenName); + +        if (mActivityChoserModelPolicy != null) { +            // Do not allow the policy to change the intent. +            Intent choiceIntentCopy = new Intent(choiceIntent); +            final boolean handled = mActivityChoserModelPolicy.onChooseActivity(this, +                    choiceIntentCopy); +            if (handled) { +                return null; +            } +        } + +        HistoricalRecord historicalRecord = new HistoricalRecord(chosenName, +                System.currentTimeMillis(), DEFAULT_HISTORICAL_RECORD_WEIGHT); +        addHisoricalRecord(historicalRecord); + +        return choiceIntent; +    } + +    /** +     * Sets the listener for choosing an activity. +     * +     * @param listener The listener. +     */ +    public void setOnChooseActivityListener(OnChooseActivityListener listener) { +        mActivityChoserModelPolicy = listener; +    } + +    /** +     * Gets the default activity, The default activity is defined as the one +     * with highest rank i.e. the first one in the list of activities that can +     * handle the intent. +     * +     * @return The default activity, <code>null</code> id not activities. +     * +     * @see #getActivity(int) +     */ +    public ResolveInfo getDefaultActivity() { +        synchronized (mInstanceLock) { +            if (!mActivites.isEmpty()) { +                return mActivites.get(0).resolveInfo; +            } +        } +        return null; +    } + +    /** +     * Sets the default activity. The default activity is set by adding a +     * historical record with weight high enough that this activity will +     * become the highest ranked. Such a strategy guarantees that the default +     * will eventually change if not used. Also the weight of the record for +     * setting a default is inflated with a constant amount to guarantee that +     * it will stay as default for awhile. +     * +     * @param index The index of the activity to set as default. +     */ +    public void setDefaultActivity(int index) { +        ActivityResolveInfo newDefaultActivity = mActivites.get(index); +        ActivityResolveInfo oldDefaultActivity = mActivites.get(0); + +        final float weight; +        if (oldDefaultActivity != null) { +            // Add a record with weight enough to boost the chosen at the top. +            weight = oldDefaultActivity.weight - newDefaultActivity.weight +                + DEFAULT_ACTIVITY_INFLATION; +        } else { +            weight = DEFAULT_HISTORICAL_RECORD_WEIGHT; +        } + +        ComponentName defaultName = new ComponentName( +                newDefaultActivity.resolveInfo.activityInfo.packageName, +                newDefaultActivity.resolveInfo.activityInfo.name); +        HistoricalRecord historicalRecord = new HistoricalRecord(defaultName, +                System.currentTimeMillis(), weight); +        addHisoricalRecord(historicalRecord); +    } + +    /** +     * Reads the history data from the backing file if the latter +     * was provided. Calling this method more than once before a call +     * to {@link #persistHistoricalData()} has been made has no effect. +     * <p> +     * <strong>Note:</strong> Historical data is read asynchronously and +     *       as soon as the reading is completed any registered +     *       {@link DataSetObserver}s will be notified. Also no historical +     *       data is read until this method is invoked. +     * <p> +     */ +    private void readHistoricalData() { +        synchronized (mInstanceLock) { +            if (!mCanReadHistoricalData || !mHistoricalRecordsChanged) { +                return; +            } +            mCanReadHistoricalData = false; +            mReadShareHistoryCalled = true; +            if (!TextUtils.isEmpty(mHistoryFileName)) { +                /*AsyncTask.*/SERIAL_EXECUTOR.execute(new HistoryLoader()); +            } +        } +    } + +    private static final Executor SERIAL_EXECUTOR = Executors.newSingleThreadExecutor(); + +    /** +     * Persists the history data to the backing file if the latter +     * was provided. Calling this method before a call to {@link #readHistoricalData()} +     * throws an exception. Calling this method more than one without choosing an +     * activity has not effect. +     * +     * @throws IllegalStateException If this method is called before a call to +     *         {@link #readHistoricalData()}. +     */ +    private void persistHistoricalData() { +        synchronized (mInstanceLock) { +            if (!mReadShareHistoryCalled) { +                throw new IllegalStateException("No preceding call to #readHistoricalData"); +            } +            if (!mHistoricalRecordsChanged) { +                return; +            } +            mHistoricalRecordsChanged = false; +            mCanReadHistoricalData = true; +            if (!TextUtils.isEmpty(mHistoryFileName)) { +                /*AsyncTask.*/SERIAL_EXECUTOR.execute(new HistoryPersister()); +            } +        } +    } + +    /** +     * Sets the sorter for ordering activities based on historical data and an intent. +     * +     * @param activitySorter The sorter. +     * +     * @see ActivitySorter +     */ +    public void setActivitySorter(ActivitySorter activitySorter) { +        synchronized (mInstanceLock) { +            if (mActivitySorter == activitySorter) { +                return; +            } +            mActivitySorter = activitySorter; +            sortActivities(); +        } +    } + +    /** +     * Sorts the activities based on history and an intent. If +     * a sorter is not specified this a default implementation is used. +     * +     * @see #setActivitySorter(ActivitySorter) +     */ +    private void sortActivities() { +        synchronized (mInstanceLock) { +            if (mActivitySorter != null && !mActivites.isEmpty()) { +                mActivitySorter.sort(mIntent, mActivites, +                        Collections.unmodifiableList(mHistoricalRecords)); +                notifyChanged(); +            } +        } +    } + +    /** +     * Sets the maximal size of the historical data. Defaults to +     * {@link #DEFAULT_HISTORY_MAX_LENGTH} +     * <p> +     *   <strong>Note:</strong> Setting this property will immediately +     *   enforce the specified max history size by dropping enough old +     *   historical records to enforce the desired size. Thus, any +     *   records that exceed the history size will be discarded and +     *   irreversibly lost. +     * </p> +     * +     * @param historyMaxSize The max history size. +     */ +    public void setHistoryMaxSize(int historyMaxSize) { +        synchronized (mInstanceLock) { +            if (mHistoryMaxSize == historyMaxSize) { +                return; +            } +            mHistoryMaxSize = historyMaxSize; +            pruneExcessiveHistoricalRecordsLocked(); +            sortActivities(); +        } +    } + +    /** +     * Gets the history max size. +     * +     * @return The history max size. +     */ +    public int getHistoryMaxSize() { +        synchronized (mInstanceLock) { +            return mHistoryMaxSize; +        } +    } + +    /** +     * Gets the history size. +     * +     * @return The history size. +     */ +    public int getHistorySize() { +        synchronized (mInstanceLock) { +            return mHistoricalRecords.size(); +        } +    } + +    /** +     * Adds a historical record. +     * +     * @param historicalRecord The record to add. +     * @return True if the record was added. +     */ +    private boolean addHisoricalRecord(HistoricalRecord historicalRecord) { +        synchronized (mInstanceLock) { +            final boolean added = mHistoricalRecords.add(historicalRecord); +            if (added) { +                mHistoricalRecordsChanged = true; +                pruneExcessiveHistoricalRecordsLocked(); +                persistHistoricalData(); +                sortActivities(); +            } +            return added; +        } +    } + +    /** +     * Prunes older excessive records to guarantee {@link #mHistoryMaxSize}. +     */ +    private void pruneExcessiveHistoricalRecordsLocked() { +        List<HistoricalRecord> choiceRecords = mHistoricalRecords; +        final int pruneCount = choiceRecords.size() - mHistoryMaxSize; +        if (pruneCount <= 0) { +            return; +        } +        mHistoricalRecordsChanged = true; +        for (int i = 0; i < pruneCount; i++) { +            HistoricalRecord prunedRecord = choiceRecords.remove(0); +            if (DEBUG) { +                Log.i(LOG_TAG, "Pruned: " + prunedRecord); +            } +        } +    } + +    /** +     * Loads the activities. +     */ +    private void loadActivitiesLocked() { +        mActivites.clear(); +        if (mIntent != null) { +            List<ResolveInfo> resolveInfos = +                mContext.getPackageManager().queryIntentActivities(mIntent, 0); +            final int resolveInfoCount = resolveInfos.size(); +            for (int i = 0; i < resolveInfoCount; i++) { +                ResolveInfo resolveInfo = resolveInfos.get(i); +                mActivites.add(new ActivityResolveInfo(resolveInfo)); +            } +            sortActivities(); +        } else { +            notifyChanged(); +        } +    } + +    /** +     * Represents a record in the history. +     */ +    public final static class HistoricalRecord { + +        /** +         * The activity name. +         */ +        public final ComponentName activity; + +        /** +         * The choice time. +         */ +        public final long time; + +        /** +         * The record weight. +         */ +        public final float weight; + +        /** +         * Creates a new instance. +         * +         * @param activityName The activity component name flattened to string. +         * @param time The time the activity was chosen. +         * @param weight The weight of the record. +         */ +        public HistoricalRecord(String activityName, long time, float weight) { +            this(ComponentName.unflattenFromString(activityName), time, weight); +        } + +        /** +         * Creates a new instance. +         * +         * @param activityName The activity name. +         * @param time The time the activity was chosen. +         * @param weight The weight of the record. +         */ +        public HistoricalRecord(ComponentName activityName, long time, float weight) { +            this.activity = activityName; +            this.time = time; +            this.weight = weight; +        } + +        @Override +        public int hashCode() { +            final int prime = 31; +            int result = 1; +            result = prime * result + ((activity == null) ? 0 : activity.hashCode()); +            result = prime * result + (int) (time ^ (time >>> 32)); +            result = prime * result + Float.floatToIntBits(weight); +            return result; +        } + +        @Override +        public boolean equals(Object obj) { +            if (this == obj) { +                return true; +            } +            if (obj == null) { +                return false; +            } +            if (getClass() != obj.getClass()) { +                return false; +            } +            HistoricalRecord other = (HistoricalRecord) obj; +            if (activity == null) { +                if (other.activity != null) { +                    return false; +                } +            } else if (!activity.equals(other.activity)) { +                return false; +            } +            if (time != other.time) { +                return false; +            } +            if (Float.floatToIntBits(weight) != Float.floatToIntBits(other.weight)) { +                return false; +            } +            return true; +        } + +        @Override +        public String toString() { +            StringBuilder builder = new StringBuilder(); +            builder.append("["); +            builder.append("; activity:").append(activity); +            builder.append("; time:").append(time); +            builder.append("; weight:").append(new BigDecimal(weight)); +            builder.append("]"); +            return builder.toString(); +        } +    } + +    /** +     * Represents an activity. +     */ +    public final class ActivityResolveInfo implements Comparable<ActivityResolveInfo> { + +        /** +         * The {@link ResolveInfo} of the activity. +         */ +        public final ResolveInfo resolveInfo; + +        /** +         * Weight of the activity. Useful for sorting. +         */ +        public float weight; + +        /** +         * Creates a new instance. +         * +         * @param resolveInfo activity {@link ResolveInfo}. +         */ +        public ActivityResolveInfo(ResolveInfo resolveInfo) { +            this.resolveInfo = resolveInfo; +        } + +        @Override +        public int hashCode() { +            return 31 + Float.floatToIntBits(weight); +        } + +        @Override +        public boolean equals(Object obj) { +            if (this == obj) { +                return true; +            } +            if (obj == null) { +                return false; +            } +            if (getClass() != obj.getClass()) { +                return false; +            } +            ActivityResolveInfo other = (ActivityResolveInfo) obj; +            if (Float.floatToIntBits(weight) != Float.floatToIntBits(other.weight)) { +                return false; +            } +            return true; +        } + +        public int compareTo(ActivityResolveInfo another) { +             return  Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight); +        } + +        @Override +        public String toString() { +            StringBuilder builder = new StringBuilder(); +            builder.append("["); +            builder.append("resolveInfo:").append(resolveInfo.toString()); +            builder.append("; weight:").append(new BigDecimal(weight)); +            builder.append("]"); +            return builder.toString(); +        } +    } + +    /** +     * Default activity sorter implementation. +     */ +    private final class DefaultSorter implements ActivitySorter { +        private static final float WEIGHT_DECAY_COEFFICIENT = 0.95f; + +        private final Map<String, ActivityResolveInfo> mPackageNameToActivityMap = +            new HashMap<String, ActivityResolveInfo>(); + +        public void sort(Intent intent, List<ActivityResolveInfo> activities, +                List<HistoricalRecord> historicalRecords) { +            Map<String, ActivityResolveInfo> packageNameToActivityMap = +                mPackageNameToActivityMap; +            packageNameToActivityMap.clear(); + +            final int activityCount = activities.size(); +            for (int i = 0; i < activityCount; i++) { +                ActivityResolveInfo activity = activities.get(i); +                activity.weight = 0.0f; +                String packageName = activity.resolveInfo.activityInfo.packageName; +                packageNameToActivityMap.put(packageName, activity); +            } + +            final int lastShareIndex = historicalRecords.size() - 1; +            float nextRecordWeight = 1; +            for (int i = lastShareIndex; i >= 0; i--) { +                HistoricalRecord historicalRecord = historicalRecords.get(i); +                String packageName = historicalRecord.activity.getPackageName(); +                ActivityResolveInfo activity = packageNameToActivityMap.get(packageName); +                if (activity != null) { +                    activity.weight += historicalRecord.weight * nextRecordWeight; +                    nextRecordWeight = nextRecordWeight * WEIGHT_DECAY_COEFFICIENT; +                } +            } + +            Collections.sort(activities); + +            if (DEBUG) { +                for (int i = 0; i < activityCount; i++) { +                    Log.i(LOG_TAG, "Sorted: " + activities.get(i)); +                } +            } +        } +    } + +    /** +     * Command for reading the historical records from a file off the UI thread. +     */ +    private final class HistoryLoader implements Runnable { + +       public void run() { +            FileInputStream fis = null; +            try { +                fis = mContext.openFileInput(mHistoryFileName); +            } catch (FileNotFoundException fnfe) { +                if (DEBUG) { +                    Log.i(LOG_TAG, "Could not open historical records file: " + mHistoryFileName); +                } +                return; +            } +            try { +                XmlPullParser parser = Xml.newPullParser(); +                parser.setInput(fis, null); + +                int type = XmlPullParser.START_DOCUMENT; +                while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { +                    type = parser.next(); +                } + +                if (!TAG_HISTORICAL_RECORDS.equals(parser.getName())) { +                    throw new XmlPullParserException("Share records file does not start with " +                            + TAG_HISTORICAL_RECORDS + " tag."); +                } + +                List<HistoricalRecord> readRecords = new ArrayList<HistoricalRecord>(); + +                while (true) { +                    type = parser.next(); +                    if (type == XmlPullParser.END_DOCUMENT) { +                        break; +                    } +                    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { +                        continue; +                    } +                    String nodeName = parser.getName(); +                    if (!TAG_HISTORICAL_RECORD.equals(nodeName)) { +                        throw new XmlPullParserException("Share records file not well-formed."); +                    } + +                    String activity = parser.getAttributeValue(null, ATTRIBUTE_ACTIVITY); +                    final long time = +                        Long.parseLong(parser.getAttributeValue(null, ATTRIBUTE_TIME)); +                    final float weight = +                        Float.parseFloat(parser.getAttributeValue(null, ATTRIBUTE_WEIGHT)); + +                    HistoricalRecord readRecord = new HistoricalRecord(activity, time, +                            weight); +                    readRecords.add(readRecord); + +                    if (DEBUG) { +                        Log.i(LOG_TAG, "Read " + readRecord.toString()); +                    } +                } + +                if (DEBUG) { +                    Log.i(LOG_TAG, "Read " + readRecords.size() + " historical records."); +                } + +                synchronized (mInstanceLock) { +                    Set<HistoricalRecord> uniqueShareRecords = +                        new LinkedHashSet<HistoricalRecord>(readRecords); + +                    // Make sure no duplicates. Example: Read a file with +                    // one record, add one record, persist the two records, +                    // add a record, read the persisted records - the +                    // read two records should not be added again. +                    List<HistoricalRecord> historicalRecords = mHistoricalRecords; +                    final int historicalRecordsCount = historicalRecords.size(); +                    for (int i = historicalRecordsCount - 1; i >= 0; i--) { +                        HistoricalRecord historicalRecord = historicalRecords.get(i); +                        uniqueShareRecords.add(historicalRecord); +                    } + +                    if (historicalRecords.size() == uniqueShareRecords.size()) { +                        return; +                    } + +                    // Make sure the oldest records go to the end. +                    historicalRecords.clear(); +                    historicalRecords.addAll(uniqueShareRecords); + +                    mHistoricalRecordsChanged = true; + +                    // Do this on the client thread since the client may be on the UI +                    // thread, wait for data changes which happen during sorting, and +                    // perform UI modification based on the data change. +                    mHandler.post(new Runnable() { +                        public void run() { +                            pruneExcessiveHistoricalRecordsLocked(); +                            sortActivities(); +                        } +                    }); +                } +            } catch (XmlPullParserException xppe) { +                Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, xppe); +            } catch (IOException ioe) { +                Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, ioe); +            } finally { +                if (fis != null) { +                    try { +                        fis.close(); +                    } catch (IOException ioe) { +                        /* ignore */ +                    } +                } +            } +        } +    } + +    /** +     * Command for persisting the historical records to a file off the UI thread. +     */ +    private final class HistoryPersister implements Runnable { + +        public void run() { +            FileOutputStream fos = null; +            List<HistoricalRecord> records = null; + +            synchronized (mInstanceLock) { +                records = new ArrayList<HistoricalRecord>(mHistoricalRecords); +            } + +            try { +                fos = mContext.openFileOutput(mHistoryFileName, Context.MODE_PRIVATE); +            } catch (FileNotFoundException fnfe) { +                Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, fnfe); +                return; +            } + +            XmlSerializer serializer = Xml.newSerializer(); + +            try { +                serializer.setOutput(fos, null); +                serializer.startDocument("UTF-8", true); +                serializer.startTag(null, TAG_HISTORICAL_RECORDS); + +                final int recordCount = records.size(); +                for (int i = 0; i < recordCount; i++) { +                    HistoricalRecord record = records.remove(0); +                    serializer.startTag(null, TAG_HISTORICAL_RECORD); +                    serializer.attribute(null, ATTRIBUTE_ACTIVITY, record.activity.flattenToString()); +                    serializer.attribute(null, ATTRIBUTE_TIME, String.valueOf(record.time)); +                    serializer.attribute(null, ATTRIBUTE_WEIGHT, String.valueOf(record.weight)); +                    serializer.endTag(null, TAG_HISTORICAL_RECORD); +                    if (DEBUG) { +                        Log.i(LOG_TAG, "Wrote " + record.toString()); +                    } +                } + +                serializer.endTag(null, TAG_HISTORICAL_RECORDS); +                serializer.endDocument(); + +                if (DEBUG) { +                    Log.i(LOG_TAG, "Wrote " + recordCount + " historical records."); +                } +            } catch (IllegalArgumentException iae) { +                Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, iae); +            } catch (IllegalStateException ise) { +                Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, ise); +            } catch (IOException ioe) { +                Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, ioe); +            } finally { +                if (fos != null) { +                    try { +                        fos.close(); +                    } catch (IOException e) { +                        /* ignore */ +                    } +                } +            } +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserView.java new file mode 100644 index 000000000..e19ea9e9e --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserView.java @@ -0,0 +1,827 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.actionbarsherlock.widget; + +import android.os.Build; +import com.actionbarsherlock.R; +import com.actionbarsherlock.internal.widget.IcsLinearLayout; +import com.actionbarsherlock.internal.widget.IcsListPopupWindow; +import com.actionbarsherlock.view.ActionProvider; +import com.actionbarsherlock.widget.ActivityChooserModel.ActivityChooserModelClient; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.database.DataSetObserver; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnGlobalLayoutListener; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.PopupWindow; +import android.widget.TextView; + +/** + * This class is a view for choosing an activity for handling a given {@link Intent}. + * <p> + * The view is composed of two adjacent buttons: + * <ul> + * <li> + * The left button is an immediate action and allows one click activity choosing. + * Tapping this button immediately executes the intent without requiring any further + * user input. Long press on this button shows a popup for changing the default + * activity. + * </li> + * <li> + * The right button is an overflow action and provides an optimized menu + * of additional activities. Tapping this button shows a popup anchored to this + * view, listing the most frequently used activities. This list is initially + * limited to a small number of items in frequency used order. The last item, + * "Show all..." serves as an affordance to display all available activities. + * </li> + * </ul> + * </p> + * + * @hide + */ +class ActivityChooserView extends ViewGroup implements ActivityChooserModelClient { + +    /** +     * An adapter for displaying the activities in an {@link AdapterView}. +     */ +    private final ActivityChooserViewAdapter mAdapter; + +    /** +     * Implementation of various interfaces to avoid publishing them in the APIs. +     */ +    private final Callbacks mCallbacks; + +    /** +     * The content of this view. +     */ +    private final IcsLinearLayout mActivityChooserContent; + +    /** +     * Stores the background drawable to allow hiding and latter showing. +     */ +    private final Drawable mActivityChooserContentBackground; + +    /** +     * The expand activities action button; +     */ +    private final FrameLayout mExpandActivityOverflowButton; + +    /** +     * The image for the expand activities action button; +     */ +    private final ImageView mExpandActivityOverflowButtonImage; + +    /** +     * The default activities action button; +     */ +    private final FrameLayout mDefaultActivityButton; + +    /** +     * The image for the default activities action button; +     */ +    private final ImageView mDefaultActivityButtonImage; + +    /** +     * The maximal width of the list popup. +     */ +    private final int mListPopupMaxWidth; + +    /** +     * The ActionProvider hosting this view, if applicable. +     */ +    ActionProvider mProvider; + +    /** +     * Observer for the model data. +     */ +    private final DataSetObserver mModelDataSetOberver = new DataSetObserver() { + +        @Override +        public void onChanged() { +            super.onChanged(); +            mAdapter.notifyDataSetChanged(); +        } +        @Override +        public void onInvalidated() { +            super.onInvalidated(); +            mAdapter.notifyDataSetInvalidated(); +        } +    }; + +    private final OnGlobalLayoutListener mOnGlobalLayoutListener = new OnGlobalLayoutListener() { +        @Override +        public void onGlobalLayout() { +            if (isShowingPopup()) { +                if (!isShown()) { +                    getListPopupWindow().dismiss(); +                } else { +                    getListPopupWindow().show(); +                    if (mProvider != null) { +                        mProvider.subUiVisibilityChanged(true); +                    } +                } +            } +        } +    }; + +    /** +     * Popup window for showing the activity overflow list. +     */ +    private IcsListPopupWindow mListPopupWindow; + +    /** +     * Listener for the dismissal of the popup/alert. +     */ +    private PopupWindow.OnDismissListener mOnDismissListener; + +    /** +     * Flag whether a default activity currently being selected. +     */ +    private boolean mIsSelectingDefaultActivity; + +    /** +     * The count of activities in the popup. +     */ +    private int mInitialActivityCount = ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT; + +    /** +     * Flag whether this view is attached to a window. +     */ +    private boolean mIsAttachedToWindow; + +    /** +     * String resource for formatting content description of the default target. +     */ +    private int mDefaultActionButtonContentDescription; + +    private final Context mContext; + +    /** +     * Create a new instance. +     * +     * @param context The application environment. +     */ +    public ActivityChooserView(Context context) { +        this(context, null); +    } + +    /** +     * Create a new instance. +     * +     * @param context The application environment. +     * @param attrs A collection of attributes. +     */ +    public ActivityChooserView(Context context, AttributeSet attrs) { +        this(context, attrs, 0); +    } + +    /** +     * Create a new instance. +     * +     * @param context The application environment. +     * @param attrs A collection of attributes. +     * @param defStyle The default style to apply to this view. +     */ +    public ActivityChooserView(Context context, AttributeSet attrs, int defStyle) { +        super(context, attrs, defStyle); +        mContext = context; + +        TypedArray attributesArray = context.obtainStyledAttributes(attrs, +                R.styleable.SherlockActivityChooserView, defStyle, 0); + +        mInitialActivityCount = attributesArray.getInt( +                R.styleable.SherlockActivityChooserView_initialActivityCount, +                ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT); + +        Drawable expandActivityOverflowButtonDrawable = attributesArray.getDrawable( +                R.styleable.SherlockActivityChooserView_expandActivityOverflowButtonDrawable); + +        attributesArray.recycle(); + +        LayoutInflater inflater = LayoutInflater.from(mContext); +        inflater.inflate(R.layout.abs__activity_chooser_view, this, true); + +        mCallbacks = new Callbacks(); + +        mActivityChooserContent = (IcsLinearLayout) findViewById(R.id.abs__activity_chooser_view_content); +        mActivityChooserContentBackground = mActivityChooserContent.getBackground(); + +        mDefaultActivityButton = (FrameLayout) findViewById(R.id.abs__default_activity_button); +        mDefaultActivityButton.setOnClickListener(mCallbacks); +        mDefaultActivityButton.setOnLongClickListener(mCallbacks); +        mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.abs__image); + +        mExpandActivityOverflowButton = (FrameLayout) findViewById(R.id.abs__expand_activities_button); +        mExpandActivityOverflowButton.setOnClickListener(mCallbacks); +        mExpandActivityOverflowButtonImage = +            (ImageView) mExpandActivityOverflowButton.findViewById(R.id.abs__image); +        mExpandActivityOverflowButtonImage.setImageDrawable(expandActivityOverflowButtonDrawable); + +        mAdapter = new ActivityChooserViewAdapter(); +        mAdapter.registerDataSetObserver(new DataSetObserver() { +            @Override +            public void onChanged() { +                super.onChanged(); +                updateAppearance(); +            } +        }); + +        Resources resources = context.getResources(); +        mListPopupMaxWidth = Math.max(resources.getDisplayMetrics().widthPixels / 2, +              resources.getDimensionPixelSize(R.dimen.abs__config_prefDialogWidth)); +    } + +    /** +     * {@inheritDoc} +     */ +    public void setActivityChooserModel(ActivityChooserModel dataModel) { +        mAdapter.setDataModel(dataModel); +        if (isShowingPopup()) { +            dismissPopup(); +            showPopup(); +        } +    } + +    /** +     * Sets the background for the button that expands the activity +     * overflow list. +     * +     * <strong>Note:</strong> Clients would like to set this drawable +     * as a clue about the action the chosen activity will perform. For +     * example, if a share activity is to be chosen the drawable should +     * give a clue that sharing is to be performed. +     * +     * @param drawable The drawable. +     */ +    public void setExpandActivityOverflowButtonDrawable(Drawable drawable) { +        mExpandActivityOverflowButtonImage.setImageDrawable(drawable); +    } + +    /** +     * Sets the content description for the button that expands the activity +     * overflow list. +     * +     * description as a clue about the action performed by the button. +     * For example, if a share activity is to be chosen the content +     * description should be something like "Share with". +     * +     * @param resourceId The content description resource id. +     */ +    public void setExpandActivityOverflowButtonContentDescription(int resourceId) { +        CharSequence contentDescription = mContext.getString(resourceId); +        mExpandActivityOverflowButtonImage.setContentDescription(contentDescription); +    } + +    /** +     * Set the provider hosting this view, if applicable. +     * @hide Internal use only +     */ +    public void setProvider(ActionProvider provider) { +        mProvider = provider; +    } + +    /** +     * Shows the popup window with activities. +     * +     * @return True if the popup was shown, false if already showing. +     */ +    public boolean showPopup() { +        if (isShowingPopup() || !mIsAttachedToWindow) { +            return false; +        } +        mIsSelectingDefaultActivity = false; +        showPopupUnchecked(mInitialActivityCount); +        return true; +    } + +    /** +     * Shows the popup no matter if it was already showing. +     * +     * @param maxActivityCount The max number of activities to display. +     */ +    private void showPopupUnchecked(int maxActivityCount) { +        if (mAdapter.getDataModel() == null) { +            throw new IllegalStateException("No data model. Did you call #setDataModel?"); +        } + +        getViewTreeObserver().addOnGlobalLayoutListener(mOnGlobalLayoutListener); + +        final boolean defaultActivityButtonShown = +            mDefaultActivityButton.getVisibility() == VISIBLE; + +        final int activityCount = mAdapter.getActivityCount(); +        final int maxActivityCountOffset = defaultActivityButtonShown ? 1 : 0; +        if (maxActivityCount != ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED +                && activityCount > maxActivityCount + maxActivityCountOffset) { +            mAdapter.setShowFooterView(true); +            mAdapter.setMaxActivityCount(maxActivityCount - 1); +        } else { +            mAdapter.setShowFooterView(false); +            mAdapter.setMaxActivityCount(maxActivityCount); +        } + +        IcsListPopupWindow popupWindow = getListPopupWindow(); +        if (!popupWindow.isShowing()) { +            if (mIsSelectingDefaultActivity || !defaultActivityButtonShown) { +                mAdapter.setShowDefaultActivity(true, defaultActivityButtonShown); +            } else { +                mAdapter.setShowDefaultActivity(false, false); +            } +            final int contentWidth = Math.min(mAdapter.measureContentWidth(), mListPopupMaxWidth); +            popupWindow.setContentWidth(contentWidth); +            popupWindow.show(); +            if (mProvider != null) { +                mProvider.subUiVisibilityChanged(true); +            } +            popupWindow.getListView().setContentDescription(mContext.getString( +                    R.string.abs__activitychooserview_choose_application)); +        } +    } + +    /** +     * Dismisses the popup window with activities. +     * +     * @return True if dismissed, false if already dismissed. +     */ +    public boolean dismissPopup() { +        if (isShowingPopup()) { +            getListPopupWindow().dismiss(); +            ViewTreeObserver viewTreeObserver = getViewTreeObserver(); +            if (viewTreeObserver.isAlive()) { +                viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener); +            } +        } +        return true; +    } + +    /** +     * Gets whether the popup window with activities is shown. +     * +     * @return True if the popup is shown. +     */ +    public boolean isShowingPopup() { +        return getListPopupWindow().isShowing(); +    } + +    @Override +    protected void onAttachedToWindow() { +        super.onAttachedToWindow(); +        ActivityChooserModel dataModel = mAdapter.getDataModel(); +        if (dataModel != null) { +            dataModel.registerObserver(mModelDataSetOberver); +        } +        mIsAttachedToWindow = true; +    } + +    @Override +    protected void onDetachedFromWindow() { +        super.onDetachedFromWindow(); +        ActivityChooserModel dataModel = mAdapter.getDataModel(); +        if (dataModel != null) { +            try { +                dataModel.unregisterObserver(mModelDataSetOberver); +            } catch (IllegalStateException e) { +                //Oh, well... fixes issue #557 +            } +        } +        ViewTreeObserver viewTreeObserver = getViewTreeObserver(); +        if (viewTreeObserver.isAlive()) { +            viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener); +        } +        mIsAttachedToWindow = false; +    } + +    @Override +    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        View child = mActivityChooserContent; +        // If the default action is not visible we want to be as tall as the +        // ActionBar so if this widget is used in the latter it will look as +        // a normal action button. +        if (mDefaultActivityButton.getVisibility() != VISIBLE) { +            heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), +                    MeasureSpec.EXACTLY); +        } +        measureChild(child, widthMeasureSpec, heightMeasureSpec); +        setMeasuredDimension(child.getMeasuredWidth(), child.getMeasuredHeight()); +    } + +    @Override +    protected void onLayout(boolean changed, int left, int top, int right, int bottom) { +        mActivityChooserContent.layout(0, 0, right - left, bottom - top); +        if (getListPopupWindow().isShowing()) { +            showPopupUnchecked(mAdapter.getMaxActivityCount()); +        } else { +            dismissPopup(); +        } +    } + +    public ActivityChooserModel getDataModel() { +        return mAdapter.getDataModel(); +    } + +    /** +     * Sets a listener to receive a callback when the popup is dismissed. +     * +     * @param listener The listener to be notified. +     */ +    public void setOnDismissListener(PopupWindow.OnDismissListener listener) { +        mOnDismissListener = listener; +    } + +    /** +     * Sets the initial count of items shown in the activities popup +     * i.e. the items before the popup is expanded. This is an upper +     * bound since it is not guaranteed that such number of intent +     * handlers exist. +     * +     * @param itemCount The initial popup item count. +     */ +    public void setInitialActivityCount(int itemCount) { +        mInitialActivityCount = itemCount; +    } + +    /** +     * Sets a content description of the default action button. This +     * resource should be a string taking one formatting argument and +     * will be used for formatting the content description of the button +     * dynamically as the default target changes. For example, a resource +     * pointing to the string "share with %1$s" will result in a content +     * description "share with Bluetooth" for the Bluetooth activity. +     * +     * @param resourceId The resource id. +     */ +    public void setDefaultActionButtonContentDescription(int resourceId) { +        mDefaultActionButtonContentDescription = resourceId; +    } + +    /** +     * Gets the list popup window which is lazily initialized. +     * +     * @return The popup. +     */ +    private IcsListPopupWindow getListPopupWindow() { +        if (mListPopupWindow == null) { +            mListPopupWindow = new IcsListPopupWindow(getContext()); +            mListPopupWindow.setAdapter(mAdapter); +            mListPopupWindow.setAnchorView(ActivityChooserView.this); +            mListPopupWindow.setModal(true); +            mListPopupWindow.setOnItemClickListener(mCallbacks); +            mListPopupWindow.setOnDismissListener(mCallbacks); +        } +        return mListPopupWindow; +    } + +    /** +     * Updates the buttons state. +     */ +    private void updateAppearance() { +        // Expand overflow button. +        if (mAdapter.getCount() > 0) { +            mExpandActivityOverflowButton.setEnabled(true); +        } else { +            mExpandActivityOverflowButton.setEnabled(false); +        } +        // Default activity button. +        final int activityCount = mAdapter.getActivityCount(); +        final int historySize = mAdapter.getHistorySize(); +        if (activityCount > 0 && historySize > 0) { +            mDefaultActivityButton.setVisibility(VISIBLE); +            ResolveInfo activity = mAdapter.getDefaultActivity(); +            PackageManager packageManager = mContext.getPackageManager(); +            mDefaultActivityButtonImage.setImageDrawable(activity.loadIcon(packageManager)); +            if (mDefaultActionButtonContentDescription != 0) { +                CharSequence label = activity.loadLabel(packageManager); +                String contentDescription = mContext.getString( +                        mDefaultActionButtonContentDescription, label); +                mDefaultActivityButton.setContentDescription(contentDescription); +            } +        } else { +            mDefaultActivityButton.setVisibility(View.GONE); +        } +        // Activity chooser content. +        if (mDefaultActivityButton.getVisibility() == VISIBLE) { +            mActivityChooserContent.setBackgroundDrawable(mActivityChooserContentBackground); +        } else { +            mActivityChooserContent.setBackgroundDrawable(null); +            mActivityChooserContent.setPadding(0, 0, 0, 0); +        } +    } + +    /** +     * Interface implementation to avoid publishing them in the APIs. +     */ +    private class Callbacks implements AdapterView.OnItemClickListener, +            View.OnClickListener, View.OnLongClickListener, PopupWindow.OnDismissListener { + +        // AdapterView#OnItemClickListener +        public void onItemClick(AdapterView<?> parent, View view, int position, long id) { +            ActivityChooserViewAdapter adapter = (ActivityChooserViewAdapter) parent.getAdapter(); +            final int itemViewType = adapter.getItemViewType(position); +            switch (itemViewType) { +                case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_FOOTER: { +                    showPopupUnchecked(ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED); +                } break; +                case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_ACTIVITY: { +                    dismissPopup(); +                    if (mIsSelectingDefaultActivity) { +                        // The item at position zero is the default already. +                        if (position > 0) { +                            mAdapter.getDataModel().setDefaultActivity(position); +                        } +                    } else { +                        // If the default target is not shown in the list, the first +                        // item in the model is default action => adjust index +                        position = mAdapter.getShowDefaultActivity() ? position : position + 1; +                        Intent launchIntent = mAdapter.getDataModel().chooseActivity(position); +                        if (launchIntent != null) { +                            mContext.startActivity(launchIntent); +                        } +                    } +                } break; +                default: +                    throw new IllegalArgumentException(); +            } +        } + +        // View.OnClickListener +        public void onClick(View view) { +            if (view == mDefaultActivityButton) { +                dismissPopup(); +                ResolveInfo defaultActivity = mAdapter.getDefaultActivity(); +                final int index = mAdapter.getDataModel().getActivityIndex(defaultActivity); +                Intent launchIntent = mAdapter.getDataModel().chooseActivity(index); +                if (launchIntent != null) { +                    mContext.startActivity(launchIntent); +                } +            } else if (view == mExpandActivityOverflowButton) { +                mIsSelectingDefaultActivity = false; +                showPopupUnchecked(mInitialActivityCount); +            } else { +                throw new IllegalArgumentException(); +            } +        } + +        // OnLongClickListener#onLongClick +        @Override +        public boolean onLongClick(View view) { +            if (view == mDefaultActivityButton) { +                if (mAdapter.getCount() > 0) { +                    mIsSelectingDefaultActivity = true; +                    showPopupUnchecked(mInitialActivityCount); +                } +            } else { +                throw new IllegalArgumentException(); +            } +            return true; +        } + +        // PopUpWindow.OnDismissListener#onDismiss +        public void onDismiss() { +            notifyOnDismissListener(); +            if (mProvider != null) { +                mProvider.subUiVisibilityChanged(false); +            } +        } + +        private void notifyOnDismissListener() { +            if (mOnDismissListener != null) { +                mOnDismissListener.onDismiss(); +            } +        } +    } + +    private static class SetActivated { +        public static void invoke(View view, boolean activated) { +            view.setActivated(activated); +        } +    } + +    private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; + +    /** +     * Adapter for backing the list of activities shown in the popup. +     */ +    private class ActivityChooserViewAdapter extends BaseAdapter { + +        public static final int MAX_ACTIVITY_COUNT_UNLIMITED = Integer.MAX_VALUE; + +        public static final int MAX_ACTIVITY_COUNT_DEFAULT = 4; + +        private static final int ITEM_VIEW_TYPE_ACTIVITY = 0; + +        private static final int ITEM_VIEW_TYPE_FOOTER = 1; + +        private static final int ITEM_VIEW_TYPE_COUNT = 3; + +        private ActivityChooserModel mDataModel; + +        private int mMaxActivityCount = MAX_ACTIVITY_COUNT_DEFAULT; + +        private boolean mShowDefaultActivity; + +        private boolean mHighlightDefaultActivity; + +        private boolean mShowFooterView; + +        public void setDataModel(ActivityChooserModel dataModel) { +            ActivityChooserModel oldDataModel = mAdapter.getDataModel(); +            if (oldDataModel != null && isShown()) { +                try { +                    oldDataModel.unregisterObserver(mModelDataSetOberver); +                } catch (IllegalStateException e) { +                    //Oh, well... fixes issue #557 +                } +            } +            mDataModel = dataModel; +            if (dataModel != null && isShown()) { +                dataModel.registerObserver(mModelDataSetOberver); +            } +            notifyDataSetChanged(); +        } + +        @Override +        public int getItemViewType(int position) { +            if (mShowFooterView && position == getCount() - 1) { +                return ITEM_VIEW_TYPE_FOOTER; +            } else { +                return ITEM_VIEW_TYPE_ACTIVITY; +            } +        } + +        @Override +        public int getViewTypeCount() { +            return ITEM_VIEW_TYPE_COUNT; +        } + +        public int getCount() { +            int count = 0; +            int activityCount = mDataModel.getActivityCount(); +            if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) { +                activityCount--; +            } +            count = Math.min(activityCount, mMaxActivityCount); +            if (mShowFooterView) { +                count++; +            } +            return count; +        } + +        public Object getItem(int position) { +            final int itemViewType = getItemViewType(position); +            switch (itemViewType) { +                case ITEM_VIEW_TYPE_FOOTER: +                    return null; +                case ITEM_VIEW_TYPE_ACTIVITY: +                    if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) { +                        position++; +                    } +                    return mDataModel.getActivity(position); +                default: +                    throw new IllegalArgumentException(); +            } +        } + +        public long getItemId(int position) { +            return position; +        } + +        public View getView(int position, View convertView, ViewGroup parent) { +            final int itemViewType = getItemViewType(position); +            switch (itemViewType) { +                case ITEM_VIEW_TYPE_FOOTER: +                    if (convertView == null || convertView.getId() != ITEM_VIEW_TYPE_FOOTER) { +                        convertView = LayoutInflater.from(getContext()).inflate( +                                R.layout.abs__activity_chooser_view_list_item, parent, false); +                        convertView.setId(ITEM_VIEW_TYPE_FOOTER); +                        TextView titleView = (TextView) convertView.findViewById(R.id.abs__title); +                        titleView.setText(mContext.getString( +                                R.string.abs__activity_chooser_view_see_all)); +                    } +                    return convertView; +                case ITEM_VIEW_TYPE_ACTIVITY: +                    if (convertView == null || convertView.getId() != R.id.abs__list_item) { +                        convertView = LayoutInflater.from(getContext()).inflate( +                                R.layout.abs__activity_chooser_view_list_item, parent, false); +                    } +                    PackageManager packageManager = mContext.getPackageManager(); +                    // Set the icon +                    ImageView iconView = (ImageView) convertView.findViewById(R.id.abs__icon); +                    ResolveInfo activity = (ResolveInfo) getItem(position); +                    iconView.setImageDrawable(activity.loadIcon(packageManager)); +                    // Set the title. +                    TextView titleView = (TextView) convertView.findViewById(R.id.abs__title); +                    titleView.setText(activity.loadLabel(packageManager)); +                    if (IS_HONEYCOMB) { +                        // Highlight the default. +                        if (mShowDefaultActivity && position == 0 && mHighlightDefaultActivity) { +                            SetActivated.invoke(convertView, true); +                        } else { +                            SetActivated.invoke(convertView, false); +                        } +                    } +                    return convertView; +                default: +                    throw new IllegalArgumentException(); +            } +        } + +        public int measureContentWidth() { +            // The user may have specified some of the target not to be shown but we +            // want to measure all of them since after expansion they should fit. +            final int oldMaxActivityCount = mMaxActivityCount; +            mMaxActivityCount = MAX_ACTIVITY_COUNT_UNLIMITED; + +            int contentWidth = 0; +            View itemView = null; + +            final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); +            final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); +            final int count = getCount(); + +            for (int i = 0; i < count; i++) { +                itemView = getView(i, itemView, null); +                itemView.measure(widthMeasureSpec, heightMeasureSpec); +                contentWidth = Math.max(contentWidth, itemView.getMeasuredWidth()); +            } + +            mMaxActivityCount = oldMaxActivityCount; + +            return contentWidth; +        } + +        public void setMaxActivityCount(int maxActivityCount) { +            if (mMaxActivityCount != maxActivityCount) { +                mMaxActivityCount = maxActivityCount; +                notifyDataSetChanged(); +            } +        } + +        public ResolveInfo getDefaultActivity() { +            return mDataModel.getDefaultActivity(); +        } + +        public void setShowFooterView(boolean showFooterView) { +            if (mShowFooterView != showFooterView) { +                mShowFooterView = showFooterView; +                notifyDataSetChanged(); +            } +        } + +        public int getActivityCount() { +            return mDataModel.getActivityCount(); +        } + +        public int getHistorySize() { +            return mDataModel.getHistorySize(); +        } + +        public int getMaxActivityCount() { +            return mMaxActivityCount; +        } + +        public ActivityChooserModel getDataModel() { +            return mDataModel; +        } + +        public void setShowDefaultActivity(boolean showDefaultActivity, +                boolean highlightDefaultActivity) { +            if (mShowDefaultActivity != showDefaultActivity +                    || mHighlightDefaultActivity != highlightDefaultActivity) { +                mShowDefaultActivity = showDefaultActivity; +                mHighlightDefaultActivity = highlightDefaultActivity; +                notifyDataSetChanged(); +            } +        } + +        public boolean getShowDefaultActivity() { +            return mShowDefaultActivity; +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/SearchView.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/SearchView.java new file mode 100644 index 000000000..c9e7897d4 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/SearchView.java @@ -0,0 +1,1811 @@ +/* + * Copyright (C) 2010 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.actionbarsherlock.widget; + +import android.app.PendingIntent; +import android.app.SearchManager; +import android.app.SearchableInfo; +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.database.Cursor; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.ResultReceiver; +import android.speech.RecognizerIntent; +import android.support.v4.view.KeyEventCompat; +import android.support.v4.widget.CursorAdapter; +import android.text.Editable; +import android.text.InputType; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.style.ImageSpan; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.AutoCompleteTextView; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; +import com.actionbarsherlock.R; +import com.actionbarsherlock.view.CollapsibleActionView; + +import java.lang.reflect.Method; +import java.util.WeakHashMap; + +import static com.actionbarsherlock.widget.SuggestionsAdapter.getColumnString; + +/** + * A widget that provides a user interface for the user to enter a search query and submit a request + * to a search provider. Shows a list of query suggestions or results, if available, and allows the + * user to pick a suggestion or result to launch into. + * + * <p> + * When the SearchView is used in an ActionBar as an action view for a collapsible menu item, it + * needs to be set to iconified by default using {@link #setIconifiedByDefault(boolean) + * setIconifiedByDefault(true)}. This is the default, so nothing needs to be done. + * </p> + * <p> + * If you want the search field to always be visible, then call setIconifiedByDefault(false). + * </p> + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For information about using {@code SearchView}, read the + * <a href="{@docRoot}guide/topics/search/index.html">Search</a> developer guide.</p> + * </div> + * + * @see android.view.MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW + * @attr ref android.R.styleable#SearchView_iconifiedByDefault + * @attr ref android.R.styleable#SearchView_imeOptions + * @attr ref android.R.styleable#SearchView_inputType + * @attr ref android.R.styleable#SearchView_maxWidth + * @attr ref android.R.styleable#SearchView_queryHint + */ +public class SearchView extends LinearLayout implements CollapsibleActionView { + +    private static final boolean DBG = false; +    private static final String LOG_TAG = "SearchView"; + +    /** +     * Private constant for removing the microphone in the keyboard. +     */ +    private static final String IME_OPTION_NO_MICROPHONE = "nm"; + +    private OnQueryTextListener mOnQueryChangeListener; +    private OnCloseListener mOnCloseListener; +    private OnFocusChangeListener mOnQueryTextFocusChangeListener; +    private OnSuggestionListener mOnSuggestionListener; +    private OnClickListener mOnSearchClickListener; + +    private boolean mIconifiedByDefault; +    private boolean mIconified; +    private CursorAdapter mSuggestionsAdapter; +    private View mSearchButton; +    private View mSubmitButton; +    private View mSearchPlate; +    private View mSubmitArea; +    private ImageView mCloseButton; +    private View mSearchEditFrame; +    private View mVoiceButton; +    private SearchAutoComplete mQueryTextView; +    private View mDropDownAnchor; +    private ImageView mSearchHintIcon; +    private boolean mSubmitButtonEnabled; +    private CharSequence mQueryHint; +    private boolean mQueryRefinement; +    private boolean mClearingFocus; +    private int mMaxWidth; +    private boolean mVoiceButtonEnabled; +    private CharSequence mOldQueryText; +    private CharSequence mUserQuery; +    private boolean mExpandedInActionView; +    private int mCollapsedImeOptions; + +    private SearchableInfo mSearchable; +    private Bundle mAppSearchData; + +    /* +    * SearchView can be set expanded before the IME is ready to be shown during +    * initial UI setup. The show operation is asynchronous to account for this. +    */ +    private Runnable mShowImeRunnable = new Runnable() { +        public void run() { +            InputMethodManager imm = (InputMethodManager) +                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + +            if (imm != null) { +                showSoftInputUnchecked(SearchView.this, imm, 0); +            } +        } +    }; + +    private Runnable mUpdateDrawableStateRunnable = new Runnable() { +        public void run() { +            updateFocusedState(); +        } +    }; + +    private Runnable mReleaseCursorRunnable = new Runnable() { +        public void run() { +            if (mSuggestionsAdapter != null && mSuggestionsAdapter instanceof SuggestionsAdapter) { +                mSuggestionsAdapter.changeCursor(null); +            } +        } +    }; + +    // For voice searching +    private final Intent mVoiceWebSearchIntent; +    private final Intent mVoiceAppSearchIntent; + +    // A weak map of drawables we've gotten from other packages, so we don't load them +    // more than once. +    private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache = +            new WeakHashMap<String, Drawable.ConstantState>(); + +    /** +     * Callbacks for changes to the query text. +     */ +    public interface OnQueryTextListener { + +        /** +         * Called when the user submits the query. This could be due to a key press on the +         * keyboard or due to pressing a submit button. +         * The listener can override the standard behavior by returning true +         * to indicate that it has handled the submit request. Otherwise return false to +         * let the SearchView handle the submission by launching any associated intent. +         * +         * @param query the query text that is to be submitted +         * +         * @return true if the query has been handled by the listener, false to let the +         * SearchView perform the default action. +         */ +        boolean onQueryTextSubmit(String query); + +        /** +         * Called when the query text is changed by the user. +         * +         * @param newText the new content of the query text field. +         * +         * @return false if the SearchView should perform the default action of showing any +         * suggestions if available, true if the action was handled by the listener. +         */ +        boolean onQueryTextChange(String newText); +    } + +    public interface OnCloseListener { + +        /** +         * The user is attempting to close the SearchView. +         * +         * @return true if the listener wants to override the default behavior of clearing the +         * text field and dismissing it, false otherwise. +         */ +        boolean onClose(); +    } + +    /** +     * Callback interface for selection events on suggestions. These callbacks +     * are only relevant when a SearchableInfo has been specified by {@link #setSearchableInfo}. +     */ +    public interface OnSuggestionListener { + +        /** +         * Called when a suggestion was selected by navigating to it. +         * @param position the absolute position in the list of suggestions. +         * +         * @return true if the listener handles the event and wants to override the default +         * behavior of possibly rewriting the query based on the selected item, false otherwise. +         */ +        boolean onSuggestionSelect(int position); + +        /** +         * Called when a suggestion was clicked. +         * @param position the absolute position of the clicked item in the list of suggestions. +         * +         * @return true if the listener handles the event and wants to override the default +         * behavior of launching any intent or submitting a search query specified on that item. +         * Return false otherwise. +         */ +        boolean onSuggestionClick(int position); +    } + +    public SearchView(Context context) { +        this(context, null); +    } + +    public SearchView(Context context, AttributeSet attrs) { +        super(context, attrs); + +        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) { +            throw new IllegalStateException("SearchView is API 8+ only."); +        } + +        LayoutInflater inflater = (LayoutInflater) context +                .getSystemService(Context.LAYOUT_INFLATER_SERVICE); +        inflater.inflate(R.layout.abs__search_view, this, true); + +        mSearchButton = findViewById(R.id.abs__search_button); +        mQueryTextView = (SearchAutoComplete) findViewById(R.id.abs__search_src_text); +        mQueryTextView.setSearchView(this); + +        mSearchEditFrame = findViewById(R.id.abs__search_edit_frame); +        mSearchPlate = findViewById(R.id.abs__search_plate); +        mSubmitArea = findViewById(R.id.abs__submit_area); +        mSubmitButton = findViewById(R.id.abs__search_go_btn); +        mCloseButton = (ImageView) findViewById(R.id.abs__search_close_btn); +        mVoiceButton = findViewById(R.id.abs__search_voice_btn); +        mSearchHintIcon = (ImageView) findViewById(R.id.abs__search_mag_icon); + +        mSearchButton.setOnClickListener(mOnClickListener); +        mCloseButton.setOnClickListener(mOnClickListener); +        mSubmitButton.setOnClickListener(mOnClickListener); +        mVoiceButton.setOnClickListener(mOnClickListener); +        mQueryTextView.setOnClickListener(mOnClickListener); + +        mQueryTextView.addTextChangedListener(mTextWatcher); +        mQueryTextView.setOnEditorActionListener(mOnEditorActionListener); +        mQueryTextView.setOnItemClickListener(mOnItemClickListener); +        mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener); +        mQueryTextView.setOnKeyListener(mTextKeyListener); +        // Inform any listener of focus changes +        mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() { + +            public void onFocusChange(View v, boolean hasFocus) { +                if (mOnQueryTextFocusChangeListener != null) { +                    mOnQueryTextFocusChangeListener.onFocusChange(SearchView.this, hasFocus); +                } +            } +        }); + +        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SherlockSearchView, 0, 0); +        setIconifiedByDefault(a.getBoolean(R.styleable.SherlockSearchView_iconifiedByDefault, true)); +        int maxWidth = a.getDimensionPixelSize(R.styleable.SherlockSearchView_android_maxWidth, -1); +        if (maxWidth != -1) { +            setMaxWidth(maxWidth); +        } +        CharSequence queryHint = a.getText(R.styleable.SherlockSearchView_queryHint); +        if (!TextUtils.isEmpty(queryHint)) { +            setQueryHint(queryHint); +        } +        int imeOptions = a.getInt(R.styleable.SherlockSearchView_android_imeOptions, -1); +        if (imeOptions != -1) { +            setImeOptions(imeOptions); +        } +        int inputType = a.getInt(R.styleable.SherlockSearchView_android_inputType, -1); +        if (inputType != -1) { +            setInputType(inputType); +        } + +        a.recycle(); + +        boolean focusable = true; + +        a = context.obtainStyledAttributes(attrs, R.styleable.SherlockView, 0, 0); +        focusable = a.getBoolean(R.styleable.SherlockView_android_focusable, focusable); +        a.recycle(); +        setFocusable(focusable); + +        // Save voice intent for later queries/launching +        mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); +        mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); +        mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, +            RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); + +        mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); +        mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + +        mDropDownAnchor = findViewById(mQueryTextView.getDropDownAnchor()); +        if (mDropDownAnchor != null) { +            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { +                mDropDownAnchor.addOnLayoutChangeListener(new OnLayoutChangeListener() { +                    @Override +                    public void onLayoutChange(View v, int left, int top, int right, int bottom, +                                                                         int oldLeft, int oldTop, int oldRight, int oldBottom) { +                        adjustDropDownSizeAndPosition(); +                    } +                }); +            } else { +                mDropDownAnchor.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { +                    @Override public void onGlobalLayout() { +                        adjustDropDownSizeAndPosition(); +                    } +                }); +            } +        } + +        updateViewsVisibility(mIconifiedByDefault); +        updateQueryHint(); +    } + +    /** +     * Sets the SearchableInfo for this SearchView. Properties in the SearchableInfo are used +     * to display labels, hints, suggestions, create intents for launching search results screens +     * and controlling other affordances such as a voice button. +     * +     * @param searchable a SearchableInfo can be retrieved from the SearchManager, for a specific +     * activity or a global search provider. +     */ +    public void setSearchableInfo(SearchableInfo searchable) { +        mSearchable = searchable; +        if (mSearchable != null) { +            updateSearchAutoComplete(); +            updateQueryHint(); +        } +        // Cache the voice search capability +        mVoiceButtonEnabled = hasVoiceSearch(); + +        if (mVoiceButtonEnabled) { +            // Disable the microphone on the keyboard, as a mic is displayed near the text box +            // TODO: use imeOptions to disable voice input when the new API will be available +            mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE); +        } +        updateViewsVisibility(isIconified()); +    } + +    /** +     * Sets the APP_DATA for legacy SearchDialog use. +     * @param appSearchData bundle provided by the app when launching the search dialog +     * @hide +     */ +    public void setAppSearchData(Bundle appSearchData) { +        mAppSearchData = appSearchData; +    } + +    /** +     * Sets the IME options on the query text field. +     * +     * @see TextView#setImeOptions(int) +     * @param imeOptions the options to set on the query text field +     * +     * @attr ref android.R.styleable#SearchView_imeOptions +     */ +    public void setImeOptions(int imeOptions) { +        mQueryTextView.setImeOptions(imeOptions); +    } + +    /** +     * Returns the IME options set on the query text field. +     * @return the ime options +     * @see TextView#setImeOptions(int) +     * +     * @attr ref android.R.styleable#SearchView_imeOptions +     */ +    public int getImeOptions() { +        return mQueryTextView.getImeOptions(); +    } + +    /** +     * Sets the input type on the query text field. +     * +     * @see TextView#setInputType(int) +     * @param inputType the input type to set on the query text field +     * +     * @attr ref android.R.styleable#SearchView_inputType +     */ +    public void setInputType(int inputType) { +        mQueryTextView.setInputType(inputType); +    } + +    /** +     * Returns the input type set on the query text field. +     * @return the input type +     * +     * @attr ref android.R.styleable#SearchView_inputType +     */ +    public int getInputType() { +        return mQueryTextView.getInputType(); +    } + +    /** @hide */ +    @Override +    public boolean requestFocus(int direction, Rect previouslyFocusedRect) { +        // Don't accept focus if in the middle of clearing focus +        if (mClearingFocus) return false; +        // Check if SearchView is focusable. +        if (!isFocusable()) return false; +        // If it is not iconified, then give the focus to the text field +        if (!isIconified()) { +            boolean result = mQueryTextView.requestFocus(direction, previouslyFocusedRect); +            if (result) { +                updateViewsVisibility(false); +            } +            return result; +        } else { +            return super.requestFocus(direction, previouslyFocusedRect); +        } +    } + +    /** @hide */ +    @Override +    public void clearFocus() { +        mClearingFocus = true; +        setImeVisibility(false); +        super.clearFocus(); +        mQueryTextView.clearFocus(); +        mClearingFocus = false; +    } + +    /** +     * Sets a listener for user actions within the SearchView. +     * +     * @param listener the listener object that receives callbacks when the user performs +     * actions in the SearchView such as clicking on buttons or typing a query. +     */ +    public void setOnQueryTextListener(OnQueryTextListener listener) { +        mOnQueryChangeListener = listener; +    } + +    /** +     * Sets a listener to inform when the user closes the SearchView. +     * +     * @param listener the listener to call when the user closes the SearchView. +     */ +    public void setOnCloseListener(OnCloseListener listener) { +        mOnCloseListener = listener; +    } + +    /** +     * Sets a listener to inform when the focus of the query text field changes. +     * +     * @param listener the listener to inform of focus changes. +     */ +    public void setOnQueryTextFocusChangeListener(OnFocusChangeListener listener) { +        mOnQueryTextFocusChangeListener = listener; +    } + +    /** +     * Sets a listener to inform when a suggestion is focused or clicked. +     * +     * @param listener the listener to inform of suggestion selection events. +     */ +    public void setOnSuggestionListener(OnSuggestionListener listener) { +        mOnSuggestionListener = listener; +    } + +    /** +     * Sets a listener to inform when the search button is pressed. This is only +     * relevant when the text field is not visible by default. Calling {@link #setIconified +     * setIconified(false)} can also cause this listener to be informed. +     * +     * @param listener the listener to inform when the search button is clicked or +     * the text field is programmatically de-iconified. +     */ +    public void setOnSearchClickListener(OnClickListener listener) { +        mOnSearchClickListener = listener; +    } + +    /** +     * Returns the query string currently in the text field. +     * +     * @return the query string +     */ +    public CharSequence getQuery() { +        return mQueryTextView.getText(); +    } + +    /** +     * Sets a query string in the text field and optionally submits the query as well. +     * +     * @param query the query string. This replaces any query text already present in the +     * text field. +     * @param submit whether to submit the query right now or only update the contents of +     * text field. +     */ +    public void setQuery(CharSequence query, boolean submit) { +        mQueryTextView.setText(query); +        if (query != null) { +            mQueryTextView.setSelection(mQueryTextView.length()); +            mUserQuery = query; +        } + +        // If the query is not empty and submit is requested, submit the query +        if (submit && !TextUtils.isEmpty(query)) { +            onSubmitQuery(); +        } +    } + +    /** +     * Sets the hint text to display in the query text field. This overrides any hint specified +     * in the SearchableInfo. +     * +     * @param hint the hint text to display +     * +     * @attr ref android.R.styleable#SearchView_queryHint +     */ +    public void setQueryHint(CharSequence hint) { +        mQueryHint = hint; +        updateQueryHint(); +    } + +    /** +     * Gets the hint text to display in the query text field. +     * @return the query hint text, if specified, null otherwise. +     * +     * @attr ref android.R.styleable#SearchView_queryHint +     */ +    public CharSequence getQueryHint() { +        if (mQueryHint != null) { +            return mQueryHint; +        } else if (mSearchable != null) { +            CharSequence hint = null; +            int hintId = mSearchable.getHintId(); +            if (hintId != 0) { +                hint = getContext().getString(hintId); +            } +            return hint; +        } +        return null; +    } + +    /** +     * Sets the default or resting state of the search field. If true, a single search icon is +     * shown by default and expands to show the text field and other buttons when pressed. Also, +     * if the default state is iconified, then it collapses to that state when the close button +     * is pressed. Changes to this property will take effect immediately. +     * +     * <p>The default value is true.</p> +     * +     * @param iconified whether the search field should be iconified by default +     * +     * @attr ref android.R.styleable#SearchView_iconifiedByDefault +     */ +    public void setIconifiedByDefault(boolean iconified) { +        if (mIconifiedByDefault == iconified) return; +        mIconifiedByDefault = iconified; +        updateViewsVisibility(iconified); +        updateQueryHint(); +    } + +    /** +     * Returns the default iconified state of the search field. +     * @return +     * +     * @attr ref android.R.styleable#SearchView_iconifiedByDefault +     */ +    public boolean isIconfiedByDefault() { +        return mIconifiedByDefault; +    } + +    /** +     * Iconifies or expands the SearchView. Any query text is cleared when iconified. This is +     * a temporary state and does not override the default iconified state set by +     * {@link #setIconifiedByDefault(boolean)}. If the default state is iconified, then +     * a false here will only be valid until the user closes the field. And if the default +     * state is expanded, then a true here will only clear the text field and not close it. +     * +     * @param iconify a true value will collapse the SearchView to an icon, while a false will +     * expand it. +     */ +    public void setIconified(boolean iconify) { +        if (iconify) { +            onCloseClicked(); +        } else { +            onSearchClicked(); +        } +    } + +    /** +     * Returns the current iconified state of the SearchView. +     * +     * @return true if the SearchView is currently iconified, false if the search field is +     * fully visible. +     */ +    public boolean isIconified() { +        return mIconified; +    } + +    /** +     * Enables showing a submit button when the query is non-empty. In cases where the SearchView +     * is being used to filter the contents of the current activity and doesn't launch a separate +     * results activity, then the submit button should be disabled. +     * +     * @param enabled true to show a submit button for submitting queries, false if a submit +     * button is not required. +     */ +    public void setSubmitButtonEnabled(boolean enabled) { +        mSubmitButtonEnabled = enabled; +        updateViewsVisibility(isIconified()); +    } + +    /** +     * Returns whether the submit button is enabled when necessary or never displayed. +     * +     * @return whether the submit button is enabled automatically when necessary +     */ +    public boolean isSubmitButtonEnabled() { +        return mSubmitButtonEnabled; +    } + +    /** +     * Specifies if a query refinement button should be displayed alongside each suggestion +     * or if it should depend on the flags set in the individual items retrieved from the +     * suggestions provider. Clicking on the query refinement button will replace the text +     * in the query text field with the text from the suggestion. This flag only takes effect +     * if a SearchableInfo has been specified with {@link #setSearchableInfo(SearchableInfo)} +     * and not when using a custom adapter. +     * +     * @param enable true if all items should have a query refinement button, false if only +     * those items that have a query refinement flag set should have the button. +     * +     * @see SearchManager#SUGGEST_COLUMN_FLAGS +     * @see SearchManager#FLAG_QUERY_REFINEMENT +     */ +    public void setQueryRefinementEnabled(boolean enable) { +        mQueryRefinement = enable; +        if (mSuggestionsAdapter instanceof SuggestionsAdapter) { +            ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement( +                    enable ? SuggestionsAdapter.REFINE_ALL : SuggestionsAdapter.REFINE_BY_ENTRY); +        } +    } + +    /** +     * Returns whether query refinement is enabled for all items or only specific ones. +     * @return true if enabled for all items, false otherwise. +     */ +    public boolean isQueryRefinementEnabled() { +        return mQueryRefinement; +    } + +    /** +     * You can set a custom adapter if you wish. Otherwise the default adapter is used to +     * display the suggestions from the suggestions provider associated with the SearchableInfo. +     * +     * @see #setSearchableInfo(SearchableInfo) +     */ +    public void setSuggestionsAdapter(CursorAdapter adapter) { +        mSuggestionsAdapter = adapter; + +        mQueryTextView.setAdapter(mSuggestionsAdapter); +    } + +    /** +     * Returns the adapter used for suggestions, if any. +     * @return the suggestions adapter +     */ +    public CursorAdapter getSuggestionsAdapter() { +        return mSuggestionsAdapter; +    } + +    /** +     * Makes the view at most this many pixels wide +     * +     * @attr ref android.R.styleable#SearchView_maxWidth +     */ +    public void setMaxWidth(int maxpixels) { +        mMaxWidth = maxpixels; + +        requestLayout(); +    } + +    /** +     * Gets the specified maximum width in pixels, if set. Returns zero if +     * no maximum width was specified. +     * @return the maximum width of the view +     * +     * @attr ref android.R.styleable#SearchView_maxWidth +     */ +    public int getMaxWidth() { +        return mMaxWidth; +    } + +    @Override +    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        // Let the standard measurements take effect in iconified state. +        if (isIconified()) { +            super.onMeasure(widthMeasureSpec, heightMeasureSpec); +            return; +        } + +        int widthMode = MeasureSpec.getMode(widthMeasureSpec); +        int width = MeasureSpec.getSize(widthMeasureSpec); + +        switch (widthMode) { +            case MeasureSpec.AT_MOST: +                // If there is an upper limit, don't exceed maximum width (explicit or implicit) +                if (mMaxWidth > 0) { +                    width = Math.min(mMaxWidth, width); +                } else { +                    width = Math.min(getPreferredWidth(), width); +                } +                break; +            case MeasureSpec.EXACTLY: +                // If an exact width is specified, still don't exceed any specified maximum width +                if (mMaxWidth > 0) { +                    width = Math.min(mMaxWidth, width); +                } +                break; +            case MeasureSpec.UNSPECIFIED: +                // Use maximum width, if specified, else preferred width +                width = mMaxWidth > 0 ? mMaxWidth : getPreferredWidth(); +                break; +        } +        widthMode = MeasureSpec.EXACTLY; +        super.onMeasure(MeasureSpec.makeMeasureSpec(width, widthMode), heightMeasureSpec); +    } + +    private int getPreferredWidth() { +        return getContext().getResources() +                .getDimensionPixelSize(R.dimen.abs__search_view_preferred_width); +    } + +    private void updateViewsVisibility(final boolean collapsed) { +        mIconified = collapsed; +        // Visibility of views that are visible when collapsed +        final int visCollapsed = collapsed ? VISIBLE : GONE; +        // Is there text in the query +        final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText()); + +        mSearchButton.setVisibility(visCollapsed); +        updateSubmitButton(hasText); +        mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE); +        mSearchHintIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE); +        updateCloseButton(); +        updateVoiceButton(!hasText); +        updateSubmitArea(); +    } + +    private boolean hasVoiceSearch() { +        if (mSearchable != null && mSearchable.getVoiceSearchEnabled()) { +            Intent testIntent = null; +            if (mSearchable.getVoiceSearchLaunchWebSearch()) { +                testIntent = mVoiceWebSearchIntent; +            } else if (mSearchable.getVoiceSearchLaunchRecognizer()) { +                testIntent = mVoiceAppSearchIntent; +            } +            if (testIntent != null) { +                ResolveInfo ri = getContext().getPackageManager().resolveActivity(testIntent, +                        PackageManager.MATCH_DEFAULT_ONLY); +                return ri != null; +            } +        } +        return false; +    } + +    private boolean isSubmitAreaEnabled() { +        return (mSubmitButtonEnabled || mVoiceButtonEnabled) && !isIconified(); +    } + +    private void updateSubmitButton(boolean hasText) { +        int visibility = GONE; +        if (mSubmitButtonEnabled && isSubmitAreaEnabled() && hasFocus() +                && (hasText || !mVoiceButtonEnabled)) { +            visibility = VISIBLE; +        } +        mSubmitButton.setVisibility(visibility); +    } + +    private void updateSubmitArea() { +        int visibility = GONE; +        if (isSubmitAreaEnabled() +                && (mSubmitButton.getVisibility() == VISIBLE +                        || mVoiceButton.getVisibility() == VISIBLE)) { +            visibility = VISIBLE; +        } +        mSubmitArea.setVisibility(visibility); +    } + +    private void updateCloseButton() { +        final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText()); +        // Should we show the close button? It is not shown if there's no focus, +        // field is not iconified by default and there is no text in it. +        final boolean showClose = hasText || (mIconifiedByDefault && !mExpandedInActionView); +        mCloseButton.setVisibility(showClose ? VISIBLE : GONE); +        mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET); +    } + +    private void postUpdateFocusedState() { +        post(mUpdateDrawableStateRunnable); +    } + +    private void updateFocusedState() { +        boolean focused = mQueryTextView.hasFocus(); +        mSearchPlate.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET); +        mSubmitArea.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET); +        invalidate(); +    } + +    @Override +    protected void onDetachedFromWindow() { +        removeCallbacks(mUpdateDrawableStateRunnable); +        post(mReleaseCursorRunnable); +        super.onDetachedFromWindow(); +    } + +    private void setImeVisibility(final boolean visible) { +        if (visible) { +            post(mShowImeRunnable); +        } else { +            removeCallbacks(mShowImeRunnable); +            InputMethodManager imm = (InputMethodManager) +                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + +            if (imm != null) { +                imm.hideSoftInputFromWindow(getWindowToken(), 0); +            } +        } +    } + +    /** +     * Called by the SuggestionsAdapter +     * @hide +     */ +    /* package */void onQueryRefine(CharSequence queryText) { +        setQuery(queryText); +    } + +    private final OnClickListener mOnClickListener = new OnClickListener() { + +        public void onClick(View v) { +            if (v == mSearchButton) { +                onSearchClicked(); +            } else if (v == mCloseButton) { +                onCloseClicked(); +            } else if (v == mSubmitButton) { +                onSubmitQuery(); +            } else if (v == mVoiceButton) { +                onVoiceClicked(); +            } else if (v == mQueryTextView) { +                forceSuggestionQuery(); +            } +        } +    }; + +    /** +     * Handles the key down event for dealing with action keys. +     * +     * @param keyCode This is the keycode of the typed key, and is the same value as +     *        found in the KeyEvent parameter. +     * @param event The complete event record for the typed key +     * +     * @return true if the event was handled here, or false if not. +     */ +    @Override +    public boolean onKeyDown(int keyCode, KeyEvent event) { +        if (mSearchable == null) { +            return false; +        } + +        // if it's an action specified by the searchable activity, launch the +        // entered query with the action key +        // TODO SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); +        // TODO if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) { +        // TODO     launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView.getText() +        // TODO             .toString()); +        // TODO     return true; +        // TODO } + +        return super.onKeyDown(keyCode, event); +    } + +    /** +     * React to the user typing "enter" or other hardwired keys while typing in +     * the search box. This handles these special keys while the edit box has +     * focus. +     */ +    View.OnKeyListener mTextKeyListener = new View.OnKeyListener() { +        public boolean onKey(View v, int keyCode, KeyEvent event) { +            // guard against possible race conditions +            if (mSearchable == null) { +                return false; +            } + +            if (DBG) { +                Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event + "), selection: " +                        + mQueryTextView.getListSelection()); +            } + +            // If a suggestion is selected, handle enter, search key, and action keys +            // as presses on the selected suggestion +            if (mQueryTextView.isPopupShowing() +                    && mQueryTextView.getListSelection() != ListView.INVALID_POSITION) { +                return onSuggestionsKey(v, keyCode, event); +            } + +            // If there is text in the query box, handle enter, and action keys +            // The search key is handled by the dialog's onKeyDown(). +            if (!mQueryTextView.isEmpty() && KeyEventCompat.hasNoModifiers(event)) { +                if (event.getAction() == KeyEvent.ACTION_UP) { +                    if (keyCode == KeyEvent.KEYCODE_ENTER) { +                        v.cancelLongPress(); + +                        // Launch as a regular search. +                        launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mQueryTextView.getText() +                                .toString()); +                        return true; +                    } +                } +                if (event.getAction() == KeyEvent.ACTION_DOWN) { +                    // TODO SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); +                    // TODO if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) { +                    // TODO     launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView +                    // TODO             .getText().toString()); +                    // TODO     return true; +                    // TODO } +                } +            } +            return false; +        } +    }; + +    /** +     * React to the user typing while in the suggestions list. First, check for +     * action keys. If not handled, try refocusing regular characters into the +     * EditText. +     */ +    private boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) { +        // guard against possible race conditions (late arrival after dismiss) +        if (mSearchable == null) { +            return false; +        } +        if (mSuggestionsAdapter == null) { +            return false; +        } +        if (event.getAction() == KeyEvent.ACTION_DOWN && KeyEventCompat.hasNoModifiers(event)) { +            // First, check for enter or search (both of which we'll treat as a +            // "click") +            if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH +                    || keyCode == KeyEvent.KEYCODE_TAB) { +                int position = mQueryTextView.getListSelection(); +                return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null); +            } + +            // Next, check for left/right moves, which we use to "return" the +            // user to the edit view +            if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { +                // give "focus" to text editor, with cursor at the beginning if +                // left key, at end if right key +                // TODO: Reverse left/right for right-to-left languages, e.g. +                // Arabic +                int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mQueryTextView +                        .length(); +                mQueryTextView.setSelection(selPoint); +                mQueryTextView.setListSelection(0); +                mQueryTextView.clearListSelection(); +                ensureImeVisible(mQueryTextView, true); + +                return true; +            } + +            // Next, check for an "up and out" move +            if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mQueryTextView.getListSelection()) { +                // TODO: restoreUserQuery(); +                // let ACTV complete the move +                return false; +            } + +            // Next, check for an "action key" +            // TODO SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); +            // TODO if ((actionKey != null) +            // TODO         && ((actionKey.getSuggestActionMsg() != null) || (actionKey +            // TODO                 .getSuggestActionMsgColumn() != null))) { +            // TODO     // launch suggestion using action key column +            // TODO     int position = mQueryTextView.getListSelection(); +            // TODO     if (position != ListView.INVALID_POSITION) { +            // TODO         Cursor c = mSuggestionsAdapter.getCursor(); +            // TODO         if (c.moveToPosition(position)) { +            // TODO             final String actionMsg = getActionKeyMessage(c, actionKey); +            // TODO             if (actionMsg != null && (actionMsg.length() > 0)) { +            // TODO                 return onItemClicked(position, keyCode, actionMsg); +            // TODO             } +            // TODO         } +            // TODO     } +            // TODO } +        } +        return false; +    } + +    /** +     * For a given suggestion and a given cursor row, get the action message. If +     * not provided by the specific row/column, also check for a single +     * definition (for the action key). +     * +     * @param c The cursor providing suggestions +     * @param actionKey The actionkey record being examined +     * +     * @return Returns a string, or null if no action key message for this +     *         suggestion +     */ +    // TODO private static String getActionKeyMessage(Cursor c, SearchableInfo.ActionKeyInfo actionKey) { +    // TODO     String result = null; +    // TODO     // check first in the cursor data, for a suggestion-specific message +    // TODO     final String column = actionKey.getSuggestActionMsgColumn(); +    // TODO     if (column != null) { +    // TODO         result = SuggestionsAdapter.getColumnString(c, column); +    // TODO     } +    // TODO     // If the cursor didn't give us a message, see if there's a single +    // TODO     // message defined +    // TODO     // for the actionkey (for all suggestions) +    // TODO     if (result == null) { +    // TODO         result = actionKey.getSuggestActionMsg(); +    // TODO     } +    // TODO     return result; +    // TODO } + +    private int getSearchIconId() { +        TypedValue outValue = new TypedValue(); +        getContext().getTheme().resolveAttribute(R.attr.searchViewSearchIcon, +                outValue, true); +        return outValue.resourceId; +    } + +    private CharSequence getDecoratedHint(CharSequence hintText) { +        // If the field is always expanded, then don't add the search icon to the hint +        if (!mIconifiedByDefault) return hintText; + +        SpannableStringBuilder ssb = new SpannableStringBuilder("   "); // for the icon +        ssb.append(hintText); +        Drawable searchIcon = getContext().getResources().getDrawable(getSearchIconId()); +        int textSize = (int) (mQueryTextView.getTextSize() * 1.25); +        searchIcon.setBounds(0, 0, textSize, textSize); +        ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); +        return ssb; +    } + +    private void updateQueryHint() { +        if (mQueryHint != null) { +            mQueryTextView.setHint(getDecoratedHint(mQueryHint)); +        } else if (mSearchable != null) { +            CharSequence hint = null; +            int hintId = mSearchable.getHintId(); +            if (hintId != 0) { +                hint = getContext().getString(hintId); +            } +            if (hint != null) { +                mQueryTextView.setHint(getDecoratedHint(hint)); +            } +        } else { +            mQueryTextView.setHint(getDecoratedHint("")); +        } +    } + +    /** +     * Updates the auto-complete text view. +     */ +    private void updateSearchAutoComplete() { +        // TODO mQueryTextView.setDropDownAnimationStyle(0); // no animation +        mQueryTextView.setThreshold(mSearchable.getSuggestThreshold()); +        mQueryTextView.setImeOptions(mSearchable.getImeOptions()); +        int inputType = mSearchable.getInputType(); +        // We only touch this if the input type is set up for text (which it almost certainly +        // should be, in the case of search!) +        if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) { +            // The existence of a suggestions authority is the proxy for "suggestions +            // are available here" +            inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE; +            if (mSearchable.getSuggestAuthority() != null) { +                inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE; +                // TYPE_TEXT_FLAG_AUTO_COMPLETE means that the text editor is performing +                // auto-completion based on its own semantics, which it will present to the user +                // as they type. This generally means that the input method should not show its +                // own candidates, and the spell checker should not be in action. The text editor +                // supplies its candidates by calling InputMethodManager.displayCompletions(), +                // which in turn will call InputMethodSession.displayCompletions(). +                inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; +            } +        } +        mQueryTextView.setInputType(inputType); +        if (mSuggestionsAdapter != null) { +            mSuggestionsAdapter.changeCursor(null); +        } +        // attach the suggestions adapter, if suggestions are available +        // The existence of a suggestions authority is the proxy for "suggestions available here" +        if (mSearchable.getSuggestAuthority() != null) { +            mSuggestionsAdapter = new SuggestionsAdapter(getContext(), +                    this, mSearchable, mOutsideDrawablesCache); +            mQueryTextView.setAdapter(mSuggestionsAdapter); +            ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement( +                    mQueryRefinement ? SuggestionsAdapter.REFINE_ALL +                    : SuggestionsAdapter.REFINE_BY_ENTRY); +        } +    } + +    /** +     * Update the visibility of the voice button.  There are actually two voice search modes, +     * either of which will activate the button. +     * @param empty whether the search query text field is empty. If it is, then the other +     * criteria apply to make the voice button visible. +     */ +    private void updateVoiceButton(boolean empty) { +        int visibility = GONE; +        if (mVoiceButtonEnabled && !isIconified() && empty) { +            visibility = VISIBLE; +            mSubmitButton.setVisibility(GONE); +        } +        mVoiceButton.setVisibility(visibility); +    } + +    private final OnEditorActionListener mOnEditorActionListener = new OnEditorActionListener() { + +        /** +         * Called when the input method default action key is pressed. +         */ +        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { +            onSubmitQuery(); +            return true; +        } +    }; + +    private void onTextChanged(CharSequence newText) { +        CharSequence text = mQueryTextView.getText(); +        mUserQuery = text; +        boolean hasText = !TextUtils.isEmpty(text); +        updateSubmitButton(hasText); +        updateVoiceButton(!hasText); +        updateCloseButton(); +        updateSubmitArea(); +        if (mOnQueryChangeListener != null && !TextUtils.equals(newText, mOldQueryText)) { +            mOnQueryChangeListener.onQueryTextChange(newText.toString()); +        } +        mOldQueryText = newText.toString(); +    } + +    private void onSubmitQuery() { +        CharSequence query = mQueryTextView.getText(); +        if (query != null && TextUtils.getTrimmedLength(query) > 0) { +            if (mOnQueryChangeListener == null +                    || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) { +                if (mSearchable != null) { +                    launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString()); +                    setImeVisibility(false); +                } +                dismissSuggestions(); +            } +        } +    } + +    private void dismissSuggestions() { +        mQueryTextView.dismissDropDown(); +    } + +    private void onCloseClicked() { +        CharSequence text = mQueryTextView.getText(); +        if (TextUtils.isEmpty(text)) { +            if (mIconifiedByDefault) { +                // If the app doesn't override the close behavior +                if (mOnCloseListener == null || !mOnCloseListener.onClose()) { +                    // hide the keyboard and remove focus +                    clearFocus(); +                    // collapse the search field +                    updateViewsVisibility(true); +                } +            } +        } else { +            mQueryTextView.setText(""); +            mQueryTextView.requestFocus(); +            setImeVisibility(true); +        } + +    } + +    private void onSearchClicked() { +        updateViewsVisibility(false); +        mQueryTextView.requestFocus(); +        setImeVisibility(true); +        if (mOnSearchClickListener != null) { +            mOnSearchClickListener.onClick(this); +        } +    } + +    private void onVoiceClicked() { +        // guard against possible race conditions +        if (mSearchable == null) { +            return; +        } +        SearchableInfo searchable = mSearchable; +        try { +            if (searchable.getVoiceSearchLaunchWebSearch()) { +                Intent webSearchIntent = createVoiceWebSearchIntent(mVoiceWebSearchIntent, +                        searchable); +                getContext().startActivity(webSearchIntent); +            } else if (searchable.getVoiceSearchLaunchRecognizer()) { +                Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent, +                        searchable); +                getContext().startActivity(appSearchIntent); +            } +        } catch (ActivityNotFoundException e) { +            // Should not happen, since we check the availability of +            // voice search before showing the button. But just in case... +            Log.w(LOG_TAG, "Could not find voice search activity"); +        } +    } + +    void onTextFocusChanged() { +        updateViewsVisibility(isIconified()); +        // Delayed update to make sure that the focus has settled down and window focus changes +        // don't affect it. A synchronous update was not working. +        postUpdateFocusedState(); +        if (mQueryTextView.hasFocus()) { +            forceSuggestionQuery(); +        } +    } + +    @Override +    public void onWindowFocusChanged(boolean hasWindowFocus) { +        super.onWindowFocusChanged(hasWindowFocus); + +        postUpdateFocusedState(); +    } + +    /** +     * {@inheritDoc} +     */ +    @Override +    public void onActionViewCollapsed() { +        clearFocus(); +        updateViewsVisibility(true); +        mQueryTextView.setImeOptions(mCollapsedImeOptions); +        mExpandedInActionView = false; +    } + +    /** +     * {@inheritDoc} +     */ +    @Override +    public void onActionViewExpanded() { +        if (mExpandedInActionView) return; + +        mExpandedInActionView = true; +        mCollapsedImeOptions = mQueryTextView.getImeOptions(); +        mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN); +        mQueryTextView.setText(""); +        setIconified(false); +    } + +    @Override +    public void onInitializeAccessibilityEvent(AccessibilityEvent event) { +        super.onInitializeAccessibilityEvent(event); +        event.setClassName(SearchView.class.getName()); +    } + +    @Override +    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { +        super.onInitializeAccessibilityNodeInfo(info); +        info.setClassName(SearchView.class.getName()); +    } + +    private void adjustDropDownSizeAndPosition() { +        if (mDropDownAnchor.getWidth() > 1) { +            Resources res = getContext().getResources(); +            int anchorPadding = mSearchPlate.getPaddingLeft(); +            Rect dropDownPadding = new Rect(); +            int iconOffset = mIconifiedByDefault +                    ? res.getDimensionPixelSize(R.dimen.abs__dropdownitem_icon_width) +                    + res.getDimensionPixelSize(R.dimen.abs__dropdownitem_text_padding_left) +                    : 0; +            mQueryTextView.getDropDownBackground().getPadding(dropDownPadding); +            mQueryTextView.setDropDownHorizontalOffset(-(dropDownPadding.left + iconOffset) +                    + anchorPadding); +            mQueryTextView.setDropDownWidth(mDropDownAnchor.getWidth() + dropDownPadding.left +                    + dropDownPadding.right + iconOffset - (anchorPadding)); +        } +    } + +    private boolean onItemClicked(int position, int actionKey, String actionMsg) { +        if (mOnSuggestionListener == null +                || !mOnSuggestionListener.onSuggestionClick(position)) { +            launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null); +            setImeVisibility(false); +            dismissSuggestions(); +            return true; +        } +        return false; +    } + +    private boolean onItemSelected(int position) { +        if (mOnSuggestionListener == null +                || !mOnSuggestionListener.onSuggestionSelect(position)) { +            rewriteQueryFromSuggestion(position); +            return true; +        } +        return false; +    } + +    private final OnItemClickListener mOnItemClickListener = new OnItemClickListener() { + +        /** +         * Implements OnItemClickListener +         */ +        public void onItemClick(AdapterView<?> parent, View view, int position, long id) { +            if (DBG) Log.d(LOG_TAG, "onItemClick() position " + position); +            onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null); +        } +    }; + +    private final OnItemSelectedListener mOnItemSelectedListener = new OnItemSelectedListener() { + +        /** +         * Implements OnItemSelectedListener +         */ +        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { +            if (DBG) Log.d(LOG_TAG, "onItemSelected() position " + position); +            SearchView.this.onItemSelected(position); +        } + +        /** +         * Implements OnItemSelectedListener +         */ +        public void onNothingSelected(AdapterView<?> parent) { +            if (DBG) +                Log.d(LOG_TAG, "onNothingSelected()"); +        } +    }; + +    /** +     * Query rewriting. +     */ +    private void rewriteQueryFromSuggestion(int position) { +        CharSequence oldQuery = mQueryTextView.getText(); +        Cursor c = mSuggestionsAdapter.getCursor(); +        if (c == null) { +            return; +        } +        if (c.moveToPosition(position)) { +            // Get the new query from the suggestion. +            CharSequence newQuery = mSuggestionsAdapter.convertToString(c); +            if (newQuery != null) { +                // The suggestion rewrites the query. +                // Update the text field, without getting new suggestions. +                setQuery(newQuery); +            } else { +                // The suggestion does not rewrite the query, restore the user's query. +                setQuery(oldQuery); +            } +        } else { +            // We got a bad position, restore the user's query. +            setQuery(oldQuery); +        } +    } + +    /** +     * Launches an intent based on a suggestion. +     * +     * @param position The index of the suggestion to create the intent from. +     * @param actionKey The key code of the action key that was pressed, +     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none. +     * @param actionMsg The message for the action key that was pressed, +     *        or <code>null</code> if none. +     * @return true if a successful launch, false if could not (e.g. bad position). +     */ +    private boolean launchSuggestion(int position, int actionKey, String actionMsg) { +        Cursor c = mSuggestionsAdapter.getCursor(); +        if ((c != null) && c.moveToPosition(position)) { + +            Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg); + +            // launch the intent +            launchIntent(intent); + +            return true; +        } +        return false; +    } + +    /** +     * Launches an intent, including any special intent handling. +     */ +    private void launchIntent(Intent intent) { +        if (intent == null) { +            return; +        } +        try { +            // If the intent was created from a suggestion, it will always have an explicit +            // component here. +            getContext().startActivity(intent); +        } catch (RuntimeException ex) { +            Log.e(LOG_TAG, "Failed launch activity: " + intent, ex); +        } +    } + +    /** +     * Sets the text in the query box, without updating the suggestions. +     */ +    private void setQuery(CharSequence query) { +        setText(mQueryTextView, query, true); +        // Move the cursor to the end +        mQueryTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length()); +    } + +    private void launchQuerySearch(int actionKey, String actionMsg, String query) { +        String action = Intent.ACTION_SEARCH; +        Intent intent = createIntent(action, null, null, query, actionKey, actionMsg); +        getContext().startActivity(intent); +    } + +    /** +     * Constructs an intent from the given information and the search dialog state. +     * +     * @param action Intent action. +     * @param data Intent data, or <code>null</code>. +     * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>. +     * @param query Intent query, or <code>null</code>. +     * @param actionKey The key code of the action key that was pressed, +     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none. +     * @param actionMsg The message for the action key that was pressed, +     *        or <code>null</code> if none. +     * @return The intent. +     */ +    private Intent createIntent(String action, Uri data, String extraData, String query, +                                                            int actionKey, String actionMsg) { +        // Now build the Intent +        Intent intent = new Intent(action); +        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); +        // We need CLEAR_TOP to avoid reusing an old task that has other activities +        // on top of the one we want. We don't want to do this in in-app search though, +        // as it can be destructive to the activity stack. +        if (data != null) { +            intent.setData(data); +        } +        intent.putExtra(SearchManager.USER_QUERY, mUserQuery); +        if (query != null) { +            intent.putExtra(SearchManager.QUERY, query); +        } +        if (extraData != null) { +            intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData); +        } +        if (mAppSearchData != null) { +            intent.putExtra(SearchManager.APP_DATA, mAppSearchData); +        } +        if (actionKey != KeyEvent.KEYCODE_UNKNOWN) { +            intent.putExtra(SearchManager.ACTION_KEY, actionKey); +            intent.putExtra(SearchManager.ACTION_MSG, actionMsg); +        } +        intent.setComponent(mSearchable.getSearchActivity()); +        return intent; +    } + +    /** +     * Create and return an Intent that can launch the voice search activity for web search. +     */ +    private Intent createVoiceWebSearchIntent(Intent baseIntent, SearchableInfo searchable) { +        Intent voiceIntent = new Intent(baseIntent); +        ComponentName searchActivity = searchable.getSearchActivity(); +        voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null +                : searchActivity.flattenToShortString()); +        return voiceIntent; +    } + +    /** +     * Create and return an Intent that can launch the voice search activity, perform a specific +     * voice transcription, and forward the results to the searchable activity. +     * +     * @param baseIntent The voice app search intent to start from +     * @return A completely-configured intent ready to send to the voice search activity +     */ +    private Intent createVoiceAppSearchIntent(Intent baseIntent, SearchableInfo searchable) { +        ComponentName searchActivity = searchable.getSearchActivity(); + +        // create the necessary intent to set up a search-and-forward operation +        // in the voice search system.   We have to keep the bundle separate, +        // because it becomes immutable once it enters the PendingIntent +        Intent queryIntent = new Intent(Intent.ACTION_SEARCH); +        queryIntent.setComponent(searchActivity); +        PendingIntent pending = PendingIntent.getActivity(getContext(), 0, queryIntent, +                PendingIntent.FLAG_ONE_SHOT); + +        // Now set up the bundle that will be inserted into the pending intent +        // when it's time to do the search.  We always build it here (even if empty) +        // because the voice search activity will always need to insert "QUERY" into +        // it anyway. +        Bundle queryExtras = new Bundle(); + +        // Now build the intent to launch the voice search.  Add all necessary +        // extras to launch the voice recognizer, and then all the necessary extras +        // to forward the results to the searchable activity +        Intent voiceIntent = new Intent(baseIntent); + +        // Add all of the configuration options supplied by the searchable's metadata +        String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM; +        String prompt = null; +        String language = null; +        int maxResults = 1; + +        Resources resources = getResources(); +        if (searchable.getVoiceLanguageModeId() != 0) { +            languageModel = resources.getString(searchable.getVoiceLanguageModeId()); +        } +        if (searchable.getVoicePromptTextId() != 0) { +            prompt = resources.getString(searchable.getVoicePromptTextId()); +        } +        if (searchable.getVoiceLanguageId() != 0) { +            language = resources.getString(searchable.getVoiceLanguageId()); +        } +        if (searchable.getVoiceMaxResults() != 0) { +            maxResults = searchable.getVoiceMaxResults(); +        } +        voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel); +        voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); +        voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language); +        voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults); +        voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null +                : searchActivity.flattenToShortString()); + +        // Add the values that configure forwarding the results +        voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending); +        voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras); + +        return voiceIntent; +    } + +    /** +     * When a particular suggestion has been selected, perform the various lookups required +     * to use the suggestion.  This includes checking the cursor for suggestion-specific data, +     * and/or falling back to the XML for defaults;  It also creates REST style Uri data when +     * the suggestion includes a data id. +     * +     * @param c The suggestions cursor, moved to the row of the user's selection +     * @param actionKey The key code of the action key that was pressed, +     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none. +     * @param actionMsg The message for the action key that was pressed, +     *        or <code>null</code> if none. +     * @return An intent for the suggestion at the cursor's position. +     */ +    private Intent createIntentFromSuggestion(Cursor c, int actionKey, String actionMsg) { +        try { +            // use specific action if supplied, or default action if supplied, or fixed default +            String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION); + +            if (action == null) { +                action = mSearchable.getSuggestIntentAction(); +            } +            if (action == null) { +                action = Intent.ACTION_SEARCH; +            } + +            // use specific data if supplied, or default data if supplied +            String data = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA); +            if (data == null) { +                data = mSearchable.getSuggestIntentData(); +            } +            // then, if an ID was provided, append it. +            if (data != null) { +                String id = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID); +                if (id != null) { +                    data = data + "/" + Uri.encode(id); +                } +            } +            Uri dataUri = (data == null) ? null : Uri.parse(data); + +            String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY); +            String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); + +            return createIntent(action, dataUri, extraData, query, actionKey, actionMsg); +        } catch (RuntimeException e ) { +            int rowNum; +            try {                       // be really paranoid now +                rowNum = c.getPosition(); +            } catch (RuntimeException e2 ) { +                rowNum = -1; +            } +            Log.w(LOG_TAG, "Search suggestions cursor at row " + rowNum + +                            " returned exception.", e); +            return null; +        } +    } + +    private void forceSuggestionQuery() { +        try { +            Method before = SearchAutoComplete.class.getMethod("doBeforeTextChanged"); +            Method after = SearchAutoComplete.class.getMethod("doAfterTextChanged"); +            before.setAccessible(true); +            after.setAccessible(true); +            before.invoke(mQueryTextView); +            after.invoke(mQueryTextView); +        } catch (Exception e) { +            // Oh well... +        } +    } + +    static boolean isLandscapeMode(Context context) { +        return context.getResources().getConfiguration().orientation +                == Configuration.ORIENTATION_LANDSCAPE; +    } + +    /** +     * Callback to watch the text field for empty/non-empty +     */ +    private TextWatcher mTextWatcher = new TextWatcher() { + +        public void beforeTextChanged(CharSequence s, int start, int before, int after) { } + +        public void onTextChanged(CharSequence s, int start, +                                                            int before, int after) { +            SearchView.this.onTextChanged(s); +        } + +        public void afterTextChanged(Editable s) { +        } +    }; + +    /** +     * Local subclass for AutoCompleteTextView. +     * @hide +     */ +    public static class SearchAutoComplete extends AutoCompleteTextView { + +        private int mThreshold; +        private SearchView mSearchView; + +        public SearchAutoComplete(Context context) { +            super(context); +            mThreshold = getThreshold(); +        } + +        public SearchAutoComplete(Context context, AttributeSet attrs) { +            super(context, attrs); +            mThreshold = getThreshold(); +        } + +        public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) { +            super(context, attrs, defStyle); +            mThreshold = getThreshold(); +        } + +        void setSearchView(SearchView searchView) { +            mSearchView = searchView; +        } + +        @Override +        public void setThreshold(int threshold) { +            super.setThreshold(threshold); +            mThreshold = threshold; +        } + +        /** +         * Returns true if the text field is empty, or contains only whitespace. +         */ +        private boolean isEmpty() { +            return TextUtils.getTrimmedLength(getText()) == 0; +        } + +        /** +         * We override this method to avoid replacing the query box text when a +         * suggestion is clicked. +         */ +        @Override +        protected void replaceText(CharSequence text) { +        } + +        /** +         * We override this method to avoid an extra onItemClick being called on +         * the drop-down's OnItemClickListener by +         * {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)} when an item is +         * clicked with the trackball. +         */ +        @Override +        public void performCompletion() { +        } + +        /** +         * We override this method to be sure and show the soft keyboard if +         * appropriate when the TextView has focus. +         */ +        @Override +        public void onWindowFocusChanged(boolean hasWindowFocus) { +            super.onWindowFocusChanged(hasWindowFocus); + +            if (hasWindowFocus && mSearchView.hasFocus() && getVisibility() == VISIBLE) { +                InputMethodManager inputManager = (InputMethodManager) getContext() +                        .getSystemService(Context.INPUT_METHOD_SERVICE); +                inputManager.showSoftInput(this, 0); +                // If in landscape mode, then make sure that +                // the ime is in front of the dropdown. +                if (isLandscapeMode(getContext())) { +                    ensureImeVisible(this, true); +                } +            } +        } + +        @Override +        protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { +            super.onFocusChanged(focused, direction, previouslyFocusedRect); +            mSearchView.onTextFocusChanged(); +        } + +        /** +         * We override this method so that we can allow a threshold of zero, +         * which ACTV does not. +         */ +        @Override +        public boolean enoughToFilter() { +            return mThreshold <= 0 || super.enoughToFilter(); +        } + +        @Override +        public boolean onKeyPreIme(int keyCode, KeyEvent event) { +            if (keyCode == KeyEvent.KEYCODE_BACK) { +                // special case for the back key, we do not even try to send it +                // to the drop down list but instead, consume it immediately +                if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { +                    KeyEvent.DispatcherState state = getKeyDispatcherState(); +                    if (state != null) { +                        state.startTracking(event, this); +                    } +                    return true; +                } else if (event.getAction() == KeyEvent.ACTION_UP) { +                    KeyEvent.DispatcherState state = getKeyDispatcherState(); +                    if (state != null) { +                        state.handleUpEvent(event); +                    } +                    if (event.isTracking() && !event.isCanceled()) { +                        mSearchView.clearFocus(); +                        mSearchView.setImeVisibility(false); +                        return true; +                    } +                } +            } +            return super.onKeyPreIme(keyCode, event); +        } + +    } + +    private static void ensureImeVisible(AutoCompleteTextView view, boolean visible) { +        try { +            Method method = AutoCompleteTextView.class.getMethod("ensureImeVisible", boolean.class); +            method.setAccessible(true); +            method.invoke(view, visible); +        } catch (Exception e) { +            //Oh well... +        } +    } + +    private static void showSoftInputUnchecked(View view, InputMethodManager imm, int flags) { +        try { +            Method method = imm.getClass().getMethod("showSoftInputUnchecked", int.class, ResultReceiver.class); +            method.setAccessible(true); +            method.invoke(imm, flags, null); +        } catch (Exception e) { +            //Fallback to public API which hopefully does mostly the same thing +            imm.showSoftInput(view, flags); +        } +    } + +    private static void setText(AutoCompleteTextView view, CharSequence text, boolean filter) { +        try { +            Method method = AutoCompleteTextView.class.getMethod("setText", CharSequence.class, boolean.class); +            method.setAccessible(true); +            method.invoke(view, text, filter); +        } catch (Exception e) { +            //Fallback to public API which hopefully does mostly the same thing +            view.setText(text); +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/ShareActionProvider.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/ShareActionProvider.java new file mode 100644 index 000000000..83e9f0ca9 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/ShareActionProvider.java @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.actionbarsherlock.widget; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; +import android.util.TypedValue; +import android.view.View; + +import com.actionbarsherlock.R; +import com.actionbarsherlock.view.ActionProvider; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.MenuItem.OnMenuItemClickListener; +import com.actionbarsherlock.view.SubMenu; +import com.actionbarsherlock.widget.ActivityChooserModel.OnChooseActivityListener; + +/** + * This is a provider for a share action. It is responsible for creating views + * that enable data sharing and also to show a sub menu with sharing activities + * if the hosting item is placed on the overflow menu. + * <p> + * Here is how to use the action provider with custom backing file in a {@link MenuItem}: + * </p> + * <p> + * <pre> + * <code> + *  // In Activity#onCreateOptionsMenu + *  public boolean onCreateOptionsMenu(Menu menu) { + *      // Get the menu item. + *      MenuItem menuItem = menu.findItem(R.id.my_menu_item); + *      // Get the provider and hold onto it to set/change the share intent. + *      mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider(); + *      // Set history different from the default before getting the action + *      // view since a call to {@link MenuItem#getActionView() MenuItem.getActionView()} calls + *      // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this + *      // line if using the default share history file is desired. + *      mShareActionProvider.setShareHistoryFileName("custom_share_history.xml"); + *      . . . + *  } + * + *  // Somewhere in the application. + *  public void doShare(Intent shareIntent) { + *      // When you want to share set the share intent. + *      mShareActionProvider.setShareIntent(shareIntent); + *  } + * </pre> + * </code> + * </p> + * <p> + * <strong>Note:</strong> While the sample snippet demonstrates how to use this provider + * in the context of a menu item, the use of the provider is not limited to menu items. + * </p> + * + * @see ActionProvider + */ +public class ShareActionProvider extends ActionProvider { + +    /** +     * Listener for the event of selecting a share target. +     */ +    public interface OnShareTargetSelectedListener { + +        /** +         * Called when a share target has been selected. The client can +         * decide whether to handle the intent or rely on the default +         * behavior which is launching it. +         * <p> +         * <strong>Note:</strong> Modifying the intent is not permitted and +         *     any changes to the latter will be ignored. +         * </p> +         * +         * @param source The source of the notification. +         * @param intent The intent for launching the chosen share target. +         * @return Whether the client has handled the intent. +         */ +        public boolean onShareTargetSelected(ShareActionProvider source, Intent intent); +    } + +    /** +     * The default for the maximal number of activities shown in the sub-menu. +     */ +    private static final int DEFAULT_INITIAL_ACTIVITY_COUNT = 4; + +    /** +     * The the maximum number activities shown in the sub-menu. +     */ +    private int mMaxShownActivityCount = DEFAULT_INITIAL_ACTIVITY_COUNT; + +    /** +     * Listener for handling menu item clicks. +     */ +    private final ShareMenuItemOnMenuItemClickListener mOnMenuItemClickListener = +        new ShareMenuItemOnMenuItemClickListener(); + +    /** +     * The default name for storing share history. +     */ +    public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml"; + +    /** +     * Context for accessing resources. +     */ +    private final Context mContext; + +    /** +     * The name of the file with share history data. +     */ +    private String mShareHistoryFileName = DEFAULT_SHARE_HISTORY_FILE_NAME; + +    private OnShareTargetSelectedListener mOnShareTargetSelectedListener; + +    private OnChooseActivityListener mOnChooseActivityListener; + +    /** +     * Creates a new instance. +     * +     * @param context Context for accessing resources. +     */ +    public ShareActionProvider(Context context) { +        super(context); +        mContext = context; +    } + +    /** +     * Sets a listener to be notified when a share target has been selected. +     * The listener can optionally decide to handle the selection and +     * not rely on the default behavior which is to launch the activity. +     * <p> +     * <strong>Note:</strong> If you choose the backing share history file +     *     you will still be notified in this callback. +     * </p> +     * @param listener The listener. +     */ +    public void setOnShareTargetSelectedListener(OnShareTargetSelectedListener listener) { +        mOnShareTargetSelectedListener = listener; +        setActivityChooserPolicyIfNeeded(); +    } + +    /** +     * {@inheritDoc} +     */ +    @Override +    public View onCreateActionView() { +        // Create the view and set its data model. +        ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName); +        ActivityChooserView activityChooserView = new ActivityChooserView(mContext); +        activityChooserView.setActivityChooserModel(dataModel); + +        // Lookup and set the expand action icon. +        TypedValue outTypedValue = new TypedValue(); +        mContext.getTheme().resolveAttribute(R.attr.actionModeShareDrawable, outTypedValue, true); +        Drawable drawable = mContext.getResources().getDrawable(outTypedValue.resourceId); +        activityChooserView.setExpandActivityOverflowButtonDrawable(drawable); +        activityChooserView.setProvider(this); + +        // Set content description. +        activityChooserView.setDefaultActionButtonContentDescription( +                R.string.abs__shareactionprovider_share_with_application); +        activityChooserView.setExpandActivityOverflowButtonContentDescription( +                R.string.abs__shareactionprovider_share_with); + +        return activityChooserView; +    } + +    /** +     * {@inheritDoc} +     */ +    @Override +    public boolean hasSubMenu() { +        return true; +    } + +    /** +     * {@inheritDoc} +     */ +    @Override +    public void onPrepareSubMenu(SubMenu subMenu) { +        // Clear since the order of items may change. +        subMenu.clear(); + +        ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName); +        PackageManager packageManager = mContext.getPackageManager(); + +        final int expandedActivityCount = dataModel.getActivityCount(); +        final int collapsedActivityCount = Math.min(expandedActivityCount, mMaxShownActivityCount); + +        // Populate the sub-menu with a sub set of the activities. +        for (int i = 0; i < collapsedActivityCount; i++) { +            ResolveInfo activity = dataModel.getActivity(i); +            subMenu.add(0, i, i, activity.loadLabel(packageManager)) +                .setIcon(activity.loadIcon(packageManager)) +                .setOnMenuItemClickListener(mOnMenuItemClickListener); +        } + +        if (collapsedActivityCount < expandedActivityCount) { +            // Add a sub-menu for showing all activities as a list item. +            SubMenu expandedSubMenu = subMenu.addSubMenu(Menu.NONE, collapsedActivityCount, +                    collapsedActivityCount, +                    mContext.getString(R.string.abs__activity_chooser_view_see_all)); +            for (int i = 0; i < expandedActivityCount; i++) { +                ResolveInfo activity = dataModel.getActivity(i); +                expandedSubMenu.add(0, i, i, activity.loadLabel(packageManager)) +                    .setIcon(activity.loadIcon(packageManager)) +                    .setOnMenuItemClickListener(mOnMenuItemClickListener); +            } +        } +    } + +    /** +     * Sets the file name of a file for persisting the share history which +     * history will be used for ordering share targets. This file will be used +     * for all view created by {@link #onCreateActionView()}. Defaults to +     * {@link #DEFAULT_SHARE_HISTORY_FILE_NAME}. Set to <code>null</code> +     * if share history should not be persisted between sessions. +     * <p> +     * <strong>Note:</strong> The history file name can be set any time, however +     * only the action views created by {@link #onCreateActionView()} after setting +     * the file name will be backed by the provided file. +     * <p> +     * +     * @param shareHistoryFile The share history file name. +     */ +    public void setShareHistoryFileName(String shareHistoryFile) { +        mShareHistoryFileName = shareHistoryFile; +        setActivityChooserPolicyIfNeeded(); +    } + +    /** +     * Sets an intent with information about the share action. Here is a +     * sample for constructing a share intent: +     * <p> +     * <pre> +     * <code> +     *  Intent shareIntent = new Intent(Intent.ACTION_SEND); +     *  shareIntent.setType("image/*"); +     *  Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg")); +     *  shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString()); +     * </pre> +     * </code> +     * </p> +     * +     * @param shareIntent The share intent. +     * +     * @see Intent#ACTION_SEND +     * @see Intent#ACTION_SEND_MULTIPLE +     */ +    public void setShareIntent(Intent shareIntent) { +        ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, +            mShareHistoryFileName); +        dataModel.setIntent(shareIntent); +    } + +    /** +     * Reusable listener for handling share item clicks. +     */ +    private class ShareMenuItemOnMenuItemClickListener implements OnMenuItemClickListener { +        @Override +        public boolean onMenuItemClick(MenuItem item) { +            ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, +                    mShareHistoryFileName); +            final int itemId = item.getItemId(); +            Intent launchIntent = dataModel.chooseActivity(itemId); +            if (launchIntent != null) { +                mContext.startActivity(launchIntent); +            } +            return true; +        } +    } + +    /** +     * Set the activity chooser policy of the model backed by the current +     * share history file if needed which is if there is a registered callback. +     */ +    private void setActivityChooserPolicyIfNeeded() { +        if (mOnShareTargetSelectedListener == null) { +            return; +        } +        if (mOnChooseActivityListener == null) { +            mOnChooseActivityListener = new ShareAcitivityChooserModelPolicy(); +        } +        ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName); +        dataModel.setOnChooseActivityListener(mOnChooseActivityListener); +    } + +    /** +     * Policy that delegates to the {@link OnShareTargetSelectedListener}, if such. +     */ +    private class ShareAcitivityChooserModelPolicy implements OnChooseActivityListener { +        @Override +        public boolean onChooseActivity(ActivityChooserModel host, Intent intent) { +            if (mOnShareTargetSelectedListener != null) { +                return mOnShareTargetSelectedListener.onShareTargetSelected( +                        ShareActionProvider.this, intent); +            } +            return false; +        } +    } +} diff --git a/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/SuggestionsAdapter.java b/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/SuggestionsAdapter.java new file mode 100644 index 000000000..bd5cbd718 --- /dev/null +++ b/libraries/ActionBarSherlock/src/com/actionbarsherlock/widget/SuggestionsAdapter.java @@ -0,0 +1,733 @@ +/* + * Copyright (C) 2009 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.actionbarsherlock.widget; + +import android.app.SearchManager; +import android.app.SearchableInfo; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.widget.ResourceCursorAdapter; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.TextUtils; +import android.text.style.TextAppearanceSpan; +import android.util.Log; +import android.util.TypedValue; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import com.actionbarsherlock.R; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.WeakHashMap; + +/** + * Provides the contents for the suggestion drop-down list. + * + * @hide + */ +class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListener { + +    private static final boolean DBG = false; +    private static final String LOG_TAG = "SuggestionsAdapter"; +    private static final int QUERY_LIMIT = 50; + +    static final int REFINE_NONE = 0; +    static final int REFINE_BY_ENTRY = 1; +    static final int REFINE_ALL = 2; + +    private SearchManager mSearchManager; +    private SearchView mSearchView; +    private Context mProviderContext; +    private WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache; +    private boolean mClosed = false; +    private int mQueryRefinement = REFINE_BY_ENTRY; + +    // URL color +    private ColorStateList mUrlColor; + +    static final int INVALID_INDEX = -1; + +    // Cached column indexes, updated when the cursor changes. +    private int mText1Col = INVALID_INDEX; +    private int mText2Col = INVALID_INDEX; +    private int mText2UrlCol = INVALID_INDEX; +    private int mIconName1Col = INVALID_INDEX; +    private int mIconName2Col = INVALID_INDEX; +    private int mFlagsCol = INVALID_INDEX; + +    // private final Runnable mStartSpinnerRunnable; +    // private final Runnable mStopSpinnerRunnable; + +    /** +     * The amount of time we delay in the filter when the user presses the delete key. +     */ +    //private static final long DELETE_KEY_POST_DELAY = 500L; + +    public SuggestionsAdapter(Context context, SearchView searchView, +                SearchableInfo mSearchable, WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache) { +        super(context, +            R.layout.abs__search_dropdown_item_icons_2line, +            null,   // no initial cursor +            true);  // auto-requery +        mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); +        mProviderContext = mContext; +        mSearchView = searchView; + +        mOutsideDrawablesCache = outsideDrawablesCache; + +        // mStartSpinnerRunnable = new Runnable() { +        // public void run() { +        // // mSearchView.setWorking(true); // TODO: +        // } +        // }; +        // +        // mStopSpinnerRunnable = new Runnable() { +        // public void run() { +        // // mSearchView.setWorking(false); // TODO: +        // } +        // }; + +        // delay 500ms when deleting +//  TODO  getFilter().setDelayer(new Filter.Delayer() { +// +//      private int mPreviousLength = 0; +// +//      public long getPostingDelay(CharSequence constraint) { +//        if (constraint == null) return 0; +// +//        long delay = constraint.length() < mPreviousLength ? DELETE_KEY_POST_DELAY : 0; +//        mPreviousLength = constraint.length(); +//        return delay; +//      } +//    }); +    } + +    /** +     * Enables query refinement for all suggestions. This means that an additional icon +     * will be shown for each entry. When clicked, the suggested text on that line will be +     * copied to the query text field. +     * <p> +     * +     * @param refineWhat which queries to refine. Possible values are {@link #REFINE_NONE}, +     * {@link #REFINE_BY_ENTRY}, and {@link #REFINE_ALL}. +     */ +    public void setQueryRefinement(int refineWhat) { +        mQueryRefinement = refineWhat; +    } + +    /** +     * Returns the current query refinement preference. +     * @return value of query refinement preference +     */ +    public int getQueryRefinement() { +        return mQueryRefinement; +    } + +    /** +     * Overridden to always return <code>false</code>, since we cannot be sure that +     * suggestion sources return stable IDs. +     */ +    @Override +    public boolean hasStableIds() { +        return false; +    } + +    /** +     * Use the search suggestions provider to obtain a live cursor.  This will be called +     * in a worker thread, so it's OK if the query is slow (e.g. round trip for suggestions). +     * The results will be processed in the UI thread and changeCursor() will be called. +     */ +    @Override +    public Cursor runQueryOnBackgroundThread(CharSequence constraint) { +        if (DBG) Log.d(LOG_TAG, "runQueryOnBackgroundThread(" + constraint + ")"); +        String query = (constraint == null) ? "" : constraint.toString(); +        /** +         * for in app search we show the progress spinner until the cursor is returned with +         * the results. +         */ +        Cursor cursor = null; +        if (mSearchView.getVisibility() != View.VISIBLE +                || mSearchView.getWindowVisibility() != View.VISIBLE) { +            return null; +        } +        //mSearchView.getWindow().getDecorView().post(mStartSpinnerRunnable); // TODO: +        try { +            cursor = getSuggestions(query, QUERY_LIMIT); +            // trigger fill window so the spinner stays up until the results are copied over and +            // closer to being ready +            if (cursor != null) { +                cursor.getCount(); +                return cursor; +            } +        } catch (RuntimeException e) { +            Log.w(LOG_TAG, "Search suggestions query threw an exception.", e); +        } +        // If cursor is null or an exception was thrown, stop the spinner and return null. +        // changeCursor doesn't get called if cursor is null +        // mSearchView.getWindow().getDecorView().post(mStopSpinnerRunnable); // TODO: +        return null; +    } + +    public Cursor getSuggestions(String query, int limit) { +        Uri.Builder uriBuilder = new Uri.Builder() +                .scheme(ContentResolver.SCHEME_CONTENT) +                .query("")  // TODO: Remove, workaround for a bug in Uri.writeToParcel() +                .fragment("");  // TODO: Remove, workaround for a bug in Uri.writeToParcel() + +        // append standard suggestion query path +        uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY); + +        // inject query, either as selection args or inline +        uriBuilder.appendPath(query); + +        if (limit > 0) { +            uriBuilder.appendQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT, String.valueOf(limit)); +        } + +        Uri uri = uriBuilder.build(); + +        // finally, make the query +        return mContext.getContentResolver().query(uri, null, null, null, null); +    } + +    public void close() { +        if (DBG) Log.d(LOG_TAG, "close()"); +        changeCursor(null); +        mClosed = true; +    } + +    @Override +    public void notifyDataSetChanged() { +        if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged"); +        super.notifyDataSetChanged(); + +        // mSearchView.onDataSetChanged(); // TODO: + +        updateSpinnerState(getCursor()); +    } + +    @Override +    public void notifyDataSetInvalidated() { +        if (DBG) Log.d(LOG_TAG, "notifyDataSetInvalidated"); +        super.notifyDataSetInvalidated(); + +        updateSpinnerState(getCursor()); +    } + +    private void updateSpinnerState(Cursor cursor) { +        Bundle extras = cursor != null ? cursor.getExtras() : null; +        if (DBG) { +            Log.d(LOG_TAG, "updateSpinnerState - extra = " +                    + (extras != null +                    ? extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS) +                    : null)); +        } +        // Check if the Cursor indicates that the query is not complete and show the spinner +        if (extras != null +                && extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)) { +            // mSearchView.getWindow().getDecorView().post(mStartSpinnerRunnable); // TODO: +            return; +        } +        // If cursor is null or is done, stop the spinner +        // mSearchView.getWindow().getDecorView().post(mStopSpinnerRunnable); // TODO: +    } + +    /** +     * Cache columns. +     */ +    @Override +    public void changeCursor(Cursor c) { +        if (DBG) Log.d(LOG_TAG, "changeCursor(" + c + ")"); + +        if (mClosed) { +            Log.w(LOG_TAG, "Tried to change cursor after adapter was closed."); +            if (c != null) c.close(); +            return; +        } + +        try { +            super.changeCursor(c); + +            if (c != null) { +                mText1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1); +                mText2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2); +                mText2UrlCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL); +                mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1); +                mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2); +                mFlagsCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_FLAGS); +            } +        } catch (Exception e) { +            Log.e(LOG_TAG, "error changing cursor and caching columns", e); +        } +    } + +    /** +     * Tags the view with cached child view look-ups. +     */ +    @Override +    public View newView(Context context, Cursor cursor, ViewGroup parent) { +        View v = super.newView(context, cursor, parent); +        v.setTag(new ChildViewCache(v)); +        return v; +    } + +    /** +     * Cache of the child views of drop-drown list items, to avoid looking up the children +     * each time the contents of a list item are changed. +     */ +    private final static class ChildViewCache { +        public final TextView mText1; +        public final TextView mText2; +        public final ImageView mIcon1; +        public final ImageView mIcon2; +        public final ImageView mIconRefine; + +        public ChildViewCache(View v) { +            mText1 = (TextView) v.findViewById(android.R.id.text1); +            mText2 = (TextView) v.findViewById(android.R.id.text2); +            mIcon1 = (ImageView) v.findViewById(android.R.id.icon1); +            mIcon2 = (ImageView) v.findViewById(android.R.id.icon2); +            mIconRefine = (ImageView) v.findViewById(R.id.edit_query); +        } +    } + +    @Override +    public void bindView(View view, Context context, Cursor cursor) { +        ChildViewCache views = (ChildViewCache) view.getTag(); + +        int flags = 0; +        if (mFlagsCol != INVALID_INDEX) { +            flags = cursor.getInt(mFlagsCol); +        } +        if (views.mText1 != null) { +            String text1 = getStringOrNull(cursor, mText1Col); +            setViewText(views.mText1, text1); +        } +        if (views.mText2 != null) { +            // First check TEXT_2_URL +            CharSequence text2 = getStringOrNull(cursor, mText2UrlCol); +            if (text2 != null) { +                text2 = formatUrl(text2); +            } else { +                text2 = getStringOrNull(cursor, mText2Col); +            } + +            // If no second line of text is indicated, allow the first line of text +            // to be up to two lines if it wants to be. +            if (TextUtils.isEmpty(text2)) { +                if (views.mText1 != null) { +                    views.mText1.setSingleLine(false); +                    views.mText1.setMaxLines(2); +                } +            } else { +                if (views.mText1 != null) { +                    views.mText1.setSingleLine(true); +                    views.mText1.setMaxLines(1); +                } +            } +            setViewText(views.mText2, text2); +        } + +        if (views.mIcon1 != null) { +            setViewDrawable(views.mIcon1, getIcon1(cursor), View.INVISIBLE); +        } +        if (views.mIcon2 != null) { +            setViewDrawable(views.mIcon2, getIcon2(cursor), View.GONE); +        } +        if (mQueryRefinement == REFINE_ALL +                || (mQueryRefinement == REFINE_BY_ENTRY +                && (flags & SearchManager.FLAG_QUERY_REFINEMENT) != 0)) { +            views.mIconRefine.setVisibility(View.VISIBLE); +            views.mIconRefine.setTag(views.mText1.getText()); +            views.mIconRefine.setOnClickListener(this); +        } else { +            views.mIconRefine.setVisibility(View.GONE); +        } +    } + +    public void onClick(View v) { +        Object tag = v.getTag(); +        if (tag instanceof CharSequence) { +            mSearchView.onQueryRefine((CharSequence) tag); +        } +    } + +    private CharSequence formatUrl(CharSequence url) { +        if (mUrlColor == null) { +            // Lazily get the URL color from the current theme. +            TypedValue colorValue = new TypedValue(); +            mContext.getTheme().resolveAttribute(R.attr.textColorSearchUrl, colorValue, true); +            mUrlColor = mContext.getResources().getColorStateList(colorValue.resourceId); +        } + +        SpannableString text = new SpannableString(url); +        text.setSpan(new TextAppearanceSpan(null, 0, 0, mUrlColor, null), +                0, url.length(), +                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); +        return text; +    } + +    private void setViewText(TextView v, CharSequence text) { +        // Set the text even if it's null, since we need to clear any previous text. +        v.setText(text); + +        if (TextUtils.isEmpty(text)) { +            v.setVisibility(View.GONE); +        } else { +            v.setVisibility(View.VISIBLE); +        } +    } + +    private Drawable getIcon1(Cursor cursor) { +        if (mIconName1Col == INVALID_INDEX) { +            return null; +        } +        String value = cursor.getString(mIconName1Col); +        Drawable drawable = getDrawableFromResourceValue(value); +        if (drawable != null) { +            return drawable; +        } +        return getDefaultIcon1(cursor); +    } + +    private Drawable getIcon2(Cursor cursor) { +        if (mIconName2Col == INVALID_INDEX) { +            return null; +        } +        String value = cursor.getString(mIconName2Col); +        return getDrawableFromResourceValue(value); +    } + +    /** +     * Sets the drawable in an image view, makes sure the view is only visible if there +     * is a drawable. +     */ +    private void setViewDrawable(ImageView v, Drawable drawable, int nullVisibility) { +        // Set the icon even if the drawable is null, since we need to clear any +        // previous icon. +        v.setImageDrawable(drawable); + +        if (drawable == null) { +            v.setVisibility(nullVisibility); +        } else { +            v.setVisibility(View.VISIBLE); + +            // This is a hack to get any animated drawables (like a 'working' spinner) +            // to animate. You have to setVisible true on an AnimationDrawable to get +            // it to start animating, but it must first have been false or else the +            // call to setVisible will be ineffective. We need to clear up the story +            // about animated drawables in the future, see http://b/1878430. +            drawable.setVisible(false, false); +            drawable.setVisible(true, false); +        } +    } + +    /** +     * Gets the text to show in the query field when a suggestion is selected. +     * +     * @param cursor The Cursor to read the suggestion data from. The Cursor should already +     *        be moved to the suggestion that is to be read from. +     * @return The text to show, or <code>null</code> if the query should not be +     *         changed when selecting this suggestion. +     */ +    @Override +    public CharSequence convertToString(Cursor cursor) { +        if (cursor == null) { +            return null; +        } + +        String query = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_QUERY); +        if (query != null) { +            return query; +        } + +        return null; +    } + +    /** +     * This method is overridden purely to provide a bit of protection against +     * flaky content providers. +     * +     * @see android.widget.ListAdapter#getView(int, View, ViewGroup) +     */ +    @Override +    public View getView(int position, View convertView, ViewGroup parent) { +        try { +            return super.getView(position, convertView, parent); +        } catch (RuntimeException e) { +            Log.w(LOG_TAG, "Search suggestions cursor threw exception.", e); +            // Put exception string in item title +            View v = newView(mContext, mCursor, parent); +            if (v != null) { +                ChildViewCache views = (ChildViewCache) v.getTag(); +                TextView tv = views.mText1; +                tv.setText(e.toString()); +            } +            return v; +        } +    } + +    /** +     * Gets a drawable given a value provided by a suggestion provider. +     * +     * This value could be just the string value of a resource id +     * (e.g., "2130837524"), in which case we will try to retrieve a drawable from +     * the provider's resources. If the value is not an integer, it is +     * treated as a Uri and opened with +     * {@link ContentResolver#openOutputStream(android.net.Uri, String)}. +     * +     * All resources and URIs are read using the suggestion provider's context. +     * +     * If the string is not formatted as expected, or no drawable can be found for +     * the provided value, this method returns null. +     * +     * @param drawableId a string like "2130837524", +     *        "android.resource://com.android.alarmclock/2130837524", +     *        or "content://contacts/photos/253". +     * @return a Drawable, or null if none found +     */ +    private Drawable getDrawableFromResourceValue(String drawableId) { +        if (drawableId == null || drawableId.length() == 0 || "0".equals(drawableId)) { +            return null; +        } +        try { +            // First, see if it's just an integer +            int resourceId = Integer.parseInt(drawableId); +            // It's an int, look for it in the cache +            String drawableUri = ContentResolver.SCHEME_ANDROID_RESOURCE +                    + "://" + mProviderContext.getPackageName() + "/" + resourceId; +            // Must use URI as cache key, since ints are app-specific +            Drawable drawable = checkIconCache(drawableUri); +            if (drawable != null) { +                return drawable; +            } +            // Not cached, find it by resource ID +            drawable = mProviderContext.getResources().getDrawable(resourceId); +            // Stick it in the cache, using the URI as key +            storeInIconCache(drawableUri, drawable); +            return drawable; +        } catch (NumberFormatException nfe) { +            // It's not an integer, use it as a URI +            Drawable drawable = checkIconCache(drawableId); +            if (drawable != null) { +                return drawable; +            } +            Uri uri = Uri.parse(drawableId); +            drawable = getDrawable(uri); +            storeInIconCache(drawableId, drawable); +            return drawable; +        } catch (Resources.NotFoundException nfe) { +            // It was an integer, but it couldn't be found, bail out +            Log.w(LOG_TAG, "Icon resource not found: " + drawableId); +            return null; +        } +    } + +    /** +     * Gets a drawable by URI, without using the cache. +     * +     * @return A drawable, or {@code null} if the drawable could not be loaded. +     */ +    private Drawable getDrawable(Uri uri) { +        try { +            String scheme = uri.getScheme(); +            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { +                // Load drawables through Resources, to get the source density information +                try { +                    return getTheDrawable(uri); +                } catch (Resources.NotFoundException ex) { +                    throw new FileNotFoundException("Resource does not exist: " + uri); +                } +            } else { +                // Let the ContentResolver handle content and file URIs. +                InputStream stream = mProviderContext.getContentResolver().openInputStream(uri); +                if (stream == null) { +                    throw new FileNotFoundException("Failed to open " + uri); +                } +                try { +                    return Drawable.createFromStream(stream, null); +                } finally { +                    try { +                        stream.close(); +                    } catch (IOException ex) { +                        Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex); +                    } +                } +            } +        } catch (FileNotFoundException fnfe) { +            Log.w(LOG_TAG, "Icon not found: " + uri + ", " + fnfe.getMessage()); +            return null; +        } +    } + +    public Drawable getTheDrawable(Uri uri) throws FileNotFoundException { +        String authority = uri.getAuthority(); +        Resources r; +        if (TextUtils.isEmpty(authority)) { +            throw new FileNotFoundException("No authority: " + uri); +        } else { +            try { +                r = mContext.getPackageManager().getResourcesForApplication(authority); +            } catch (NameNotFoundException ex) { +                throw new FileNotFoundException("No package found for authority: " + uri); +            } +        } +        List<String> path = uri.getPathSegments(); +        if (path == null) { +            throw new FileNotFoundException("No path: " + uri); +        } +        int len = path.size(); +        int id; +        if (len == 1) { +            try { +                id = Integer.parseInt(path.get(0)); +            } catch (NumberFormatException e) { +                throw new FileNotFoundException("Single path segment is not a resource ID: " + uri); +            } +        } else if (len == 2) { +            id = r.getIdentifier(path.get(1), path.get(0), authority); +        } else { +            throw new FileNotFoundException("More than two path segments: " + uri); +        } +        if (id == 0) { +            throw new FileNotFoundException("No resource found for: " + uri); +        } +        return r.getDrawable(id); +    } + +    private Drawable checkIconCache(String resourceUri) { +        Drawable.ConstantState cached = mOutsideDrawablesCache.get(resourceUri); +        if (cached == null) { +            return null; +        } +        if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + resourceUri); +        return cached.newDrawable(); +    } + +    private void storeInIconCache(String resourceUri, Drawable drawable) { +        if (drawable != null) { +            mOutsideDrawablesCache.put(resourceUri, drawable.getConstantState()); +        } +    } + +    /** +     * Gets the left-hand side icon that will be used for the current suggestion +     * if the suggestion contains an icon column but no icon or a broken icon. +     * +     * @param cursor A cursor positioned at the current suggestion. +     * @return A non-null drawable. +     */ +    private Drawable getDefaultIcon1(Cursor cursor) { +        // Fall back to a default icon +        return mContext.getPackageManager().getDefaultActivityIcon(); +    } + +    /** +     * Gets the activity or application icon for an activity. +     * Uses the local icon cache for fast repeated lookups. +     * +     * @param component Name of an activity. +     * @return A drawable, or {@code null} if neither the activity nor the application +     *         has an icon set. +     */ +    private Drawable getActivityIconWithCache(ComponentName component) { +        // First check the icon cache +        String componentIconKey = component.flattenToShortString(); +        // Using containsKey() since we also store null values. +        if (mOutsideDrawablesCache.containsKey(componentIconKey)) { +            Drawable.ConstantState cached = mOutsideDrawablesCache.get(componentIconKey); +            return cached == null ? null : cached.newDrawable(mProviderContext.getResources()); +        } +        // Then try the activity or application icon +        Drawable drawable = getActivityIcon(component); +        // Stick it in the cache so we don't do this lookup again. +        Drawable.ConstantState toCache = drawable == null ? null : drawable.getConstantState(); +        mOutsideDrawablesCache.put(componentIconKey, toCache); +        return drawable; +    } + +    /** +     * Gets the activity or application icon for an activity. +     * +     * @param component Name of an activity. +     * @return A drawable, or {@code null} if neither the acitivy or the application +     *         have an icon set. +     */ +    private Drawable getActivityIcon(ComponentName component) { +        PackageManager pm = mContext.getPackageManager(); +        final ActivityInfo activityInfo; +        try { +            activityInfo = pm.getActivityInfo(component, PackageManager.GET_META_DATA); +        } catch (NameNotFoundException ex) { +            Log.w(LOG_TAG, ex.toString()); +            return null; +        } +        int iconId = activityInfo.getIconResource(); +        if (iconId == 0) return null; +        String pkg = component.getPackageName(); +        Drawable drawable = pm.getDrawable(pkg, iconId, activityInfo.applicationInfo); +        if (drawable == null) { +            Log.w(LOG_TAG, "Invalid icon resource " + iconId + " for " +                    + component.flattenToShortString()); +            return null; +        } +        return drawable; +    } + +    /** +     * Gets the value of a string column by name. +     * +     * @param cursor Cursor to read the value from. +     * @param columnName The name of the column to read. +     * @return The value of the given column, or <code>null</null> +     *         if the cursor does not contain the given column. +     */ +    public static String getColumnString(Cursor cursor, String columnName) { +        int col = cursor.getColumnIndex(columnName); +        return getStringOrNull(cursor, col); +    } + +    private static String getStringOrNull(Cursor cursor, int col) { +        if (col == INVALID_INDEX) { +            return null; +        } +        try { +            return cursor.getString(col); +        } catch (Exception e) { +            Log.e(LOG_TAG, +                    "unexpected error retrieving valid column from cursor, " +                            + "did the remote process die?", e); +            return null; +        } +    } +} diff --git a/libraries/ActionBarSherlock/test/com/actionbarsherlock/internal/ManifestParsingTest.java b/libraries/ActionBarSherlock/test/com/actionbarsherlock/internal/ManifestParsingTest.java new file mode 100644 index 000000000..47475c574 --- /dev/null +++ b/libraries/ActionBarSherlock/test/com/actionbarsherlock/internal/ManifestParsingTest.java @@ -0,0 +1,37 @@ +package com.actionbarsherlock.internal; + +import org.junit.Test; + +import static com.actionbarsherlock.internal.ActionBarSherlockCompat.cleanActivityName; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +public class ManifestParsingTest { +    @Test +    public void testFullyQualifiedClassName() { +        String expected = "com.other.package.SomeClass"; +        String actual = cleanActivityName("com.jakewharton.test", "com.other.package.SomeClass"); +        assertThat(expected, equalTo(actual)); +    } + +    @Test +    public void testFullyQualifiedClassNameSamePackage() { +        String expected = "com.jakewharton.test.SomeClass"; +        String actual = cleanActivityName("com.jakewharton.test", "com.jakewharton.test.SomeClass"); +        assertThat(expected, equalTo(actual)); +    } + +    @Test +    public void testUnqualifiedClassName() { +        String expected = "com.jakewharton.test.SomeClass"; +        String actual = cleanActivityName("com.jakewharton.test", "SomeClass"); +        assertThat(expected, equalTo(actual)); +    } + +    @Test +    public void testRelativeClassName() { +        String expected = "com.jakewharton.test.ui.SomeClass"; +        String actual = cleanActivityName("com.jakewharton.test", ".ui.SomeClass"); +        assertThat(expected, equalTo(actual)); +    } +}
\ No newline at end of file  | 
