diff --git a/actionbarsherlock/.gitignore b/actionbarsherlock/.gitignore new file mode 100644 index 000000000..e2c834f53 --- /dev/null +++ b/actionbarsherlock/.gitignore @@ -0,0 +1,29 @@ +#Android generated +bin +gen + +#Eclipse +.project +.classpath +.settings + +#IntelliJ IDEA +.idea +*.iml +*.ipr +*.iws + +#Maven +target +release.properties +pom.xml.* + +website/_site + +#Ant +build.xml +local.properties +proguard.cfg + +#OSX +.DS_Store diff --git a/actionbarsherlock/CHANGELOG.md b/actionbarsherlock/CHANGELOG.md new file mode 100644 index 000000000..6496e4719 --- /dev/null +++ b/actionbarsherlock/CHANGELOG.md @@ -0,0 +1,343 @@ +Change Log +=============================================================================== + +Version 3.4.2 *(2001-11-10)* +---------------------------- + + * Fix: Stacked action bar now properly sets the tab bar background based on + the theme. + + +Version 3.4.1 *(2011-11-09)* +---------------------------- + + * The `makeFragmentName` method in `FragmentPagerAdapter` has been changed to + `public` scope to allow for easier access to your fragments that it is + managing. + * Action bar will now animate when calling `show()` or `hide()`. + * `SherlockPreferenceActivity` now provides full fragment and loader support. + * Examples for the plugins are now in their own sample application. + * Fix: Home icon no longer erroneously clipped when it exceeds the size of the + action bar. + * Fix: Tabs will now scroll horizontally to mimic the native action bar + behavior. + * Fix: Plugins now properly DO NOT inline their `R.java` integer constants. + * Fix: Tabs below the action bar are now styled with a default background so + that they do not incorrectly inherit an applied background unless explicity + declared. + + +Version 3.4.0 *(2011-10-30)* +---------------------------- + + * Library now uses the `r4` version of the compatibility library for its base. + Ice Cream Sandwich-specific implementations are currently disabled, however, + but will be added in a future version of the library. + * Context menu callbacks now use the support version of `MenuItem` to maintain + consistency. + * Added preference plugin which provides an action bar enhanced preference + screen. + * Fix: `abHomeLayout` theme attribute is now honored. + * Fix: `onPrepareOptionsMenu` is now properly dispatched upon menu + invalidation. + + +Version 3.3.1 *(2011-10-20)* +---------------------------- + +ADT 14 is now required. Maven 3 is required if building from the command line. + + * XML-defined `onClick` attributes will now check for an `onClick` method that + takes an `android.support.v4.view.MenuItem` instance. + * Tabs on medium screens in landscape now display inline rather than below the + action bar to mirror how Android 4.0 behaves with the same configuration. + * Fix: Menu inflater properly checks activity context for `onClick` method + declared in the XML. + * Fix: Dialog fragment properly saves its `showDialog` state when not being + used as a popup. + * Fix: Return `-1` when in tab navigation but no tab is selected. This brings + the library in line with the post-3.0 behavior. + * Fix: Removing a menu group no longer throws an `IndexOutOfBoundsException`. + * Fix: `getSelectedTab` and `getTabAt` no longer throw `NullPointerException`s + on post-3.0 when no tab was selected or no tab existed at the specified + position, respectively. + * Fix: `findFragmentById` now properly returns fragments attached to + `android.R.id.content` when run on pre-3.0 devices. + + +Version 3.3.0 *(2011-10-11)* +---------------------------- + + * Tabs are now displayed below the action bar on all medium-screen devices and + portrait large-screen devices. + * Fix: Dialog fragments no longer throw an `IllegalStateException` when being + used as a regular fragment (i.e., not as a popup). See + [StackOverflow](http://stackoverflow.com/questions/5637894/dialogfragments-with-devices-api-level-11/7560686#7560686) + for more information. + * Fix: Popping a fragment off of the back stack now properly assigns its parent + activity. + * Fix: An activity result no longer causes a `NullPointerException` when the + target fragment no longer exists. + * Fix: Action item dividers are now properly initially hidden when their + associated action items are as well. + + +Version 3.2.3 *(2011-09-16)* +---------------------------- + + * Fix: Fragments in a `ViewPager` that contributed items to the options menu + were caught in a race condition causing inconsistent results when a new page + was selected. This regression was introduced in version 3.2.2. + + +Version 3.2.2 *(2011-09-15)* +---------------------------- + + * Fix: Side-effects related to using `FragmentMapActivity` due to how it was + referencing resources from the main library. + * Fix: Fragments adjacent to the currently selected fragment in a `ViewPager` + no longer receive context menu events. + * Fix: Eliminate exception when inflating context menus on 3.0+ when using + `getMenuInflater()`. + * Fix: `ViewPager` now determines whether or not an activity menu invalidation + is required independently of whether or not fragments were created or + destroyed. This should fix an edge case where an activity with a `ViewPager` + containing only two fragments would not get its menu properly invalidated. + + +Version 3.2.1 *(2011-09-12)* +---------------------------- + + * Fix: Action mode API incorrectly using the native `Menu` and `MenuItem` + classes causing an easy pitfall for `ClassCastExceptions`. + * Fix: Large action bar backgrounds increasing the size beyond that alloted in + the theme. + + +Version 3.2.0 *(2011-09-05)* +---------------------------- + + * Added support for `MapView` and the Google APIs through the use of + `FragmentMapActivity`. If you are using a map within a fragment you must + ensure it is always attached to an activity which extends from this new base + class. + + Since supporting maps requires compiling against the Google APIs, this + functionality is implemented in the form of a plugin which is to be used + alongside the normal library. You can choose to add it as an additional + library project or by including it as a `.jar`. Maven users may simply + include the additional dependency (artifactId: `plugin-maps`). + * Fix: Fragments adjacent to the currently selected fragment in a `ViewPager` + no longer contribute to the activity menu. + * `ActionBar.Tab` has been changed from an interface to an abstract class to + mirror its native counterpart. + + +Version 3.1.3 *(2011-08-14)* +---------------------------- + + * Renamed all resources to be prefixed with `abs__` to avoid conflicts when + including in your project. + * Fix: Action bar background being set on two views causing artifacts to remain + on screen when the action bar was hidden. + * Fix: Incorrect sub-menu item being selected by default when the sub-menu was + triggered from the native options menu on pre-3.0. + * Fix: `MenuItem.setVisible` now properly updates the associated action item and + native menu item visible state. + * Fix: Adding items to a menu now honors its ordering and category. + * Fix: Fragment options item selected callback now uses the proper version of + `MenuItem`. + + +Version 3.1.2 *(2011-08-07)* +---------------------------- + + * Fix: `MenuItem.getMenuInfo()` was throwing runtime exception. Will now just + return `null`. + * Fix: Dragging over a `WebView` contained in a `ViewPager` would not register. + * Fix: Inflation of context menu incorrectly being handled by the custom menu + inflater for the library. + + +Version 3.1.1 *(2011-07-31)* +---------------------------- + + * Fix: `MenuItem.getSubMenu` now returns a support instance rather than a + native instance. + * Fix: Fragment methods `onAttach` and `onInflate` incorrectly regressed to use + `Activity` instead of a `FragmentActivity` in their method signatures. + * Fix: Retained fragments not being re-attached on pre-3.0 when attached to + `android.R.id.content` upon activity recreation. + * Fix: `onPrepareOptionsMenu` not dispatched to fragments. This still will only + occur if the activity method returns true (which is the default). + * Fix: `Menu.findItem` not returning `null` when the item was not found on + Android 3.0+. + + +Version 3.1.0 *(2011-07-22)* +---------------------------- + +Due to shortcomings in the Android theming system, a small change must be made +in how this library handles themes. If you were using a custom style for +`actionBarStyle` you must now specify its attributes in the root of the theme +and prefix them with 'ab'. + +You can see an example of this in the `SherlockCustom` theme in +`samples/demos/res/values/styles.xml`. + + * Library now uses the `r3` version of the compatibility library for its base. + * `actionBarStyle` is no longer a valid theme attribute (see note above). + * Added the demo project included with the new compatibility library under + `samples/demos/` and merged in the old 'featuredemo'. + * Dividers are now shown on pre-3.0 devices between all action items. + * `Window.FEATURE_ACTION_BAR_OVERLAY` is now honored on pre-3.0 devices. + * Inflation of XML menu resources will now honor `android:actionLayout` and + `android:actionViewClass` attributes. + * Buttons for displaying the determinate and indeterminate progress bars have + been added to the feature toggle demo. + * Added support for indeterminate progress bar. Due to the `final` modifier on + the native type, you must use `setIndeterminateProgressBarVisibility(Boolean)` + and pass `Boolean.TRUE` or `Boolean.FALSE`. + * Fix: `MenuBuilder#removeItem(int)` and `MenuBuilder#findItem(int)` throwing + `IndexOutOfBoundsException`s when the item was not found. + * Fix: Theme attributes for home item data (e.g., icon, logo) will not be + overwritten by the special `MenuItem` instance for home. + * Fix: Native strings can now be specified for an XML menu `` in + `android:title` and `android:titleCondensed`. + * `Window.FEATURE_ENABLE_ACTION_BAR_WATSON_TEXT` is now + `Window.FEATURE_ACTION_BAR_ITEM_TEXT`. + * `Widget.Sherlock.Spinner.DropDown.ActionBar` and + `Widget.Sherlock.Light.Spinner.DropDown.ActionBar` styles are now + `Widget.Sherlock.Spinner` and `Widget.Sherlock.Light.Spinner`, respectively. + * `Widget.Sherlock.ActionBarView_TabXXX` styles are now + `Widget.Sherlock.ActionBar.TabXXX`. + + +Version 3.0.3 *(2011-07-17)* +---------------------------- + +This version is a hotfix for incompatibilities introduced with the SDKs for +3.1 r2 and 3.2 r1. Due to unavoidable changes in the underlying SDK, the library +must now be compiled against API level 13. + + * `actionModeStyle` and `actionModePopupWindowStyle` are no longer valid theme + attributes. + + +Version 3.0.2 *(2011-06-23)* +---------------------------- + + * Sub-menus for action items are now shown in a list dialog. + * Moved certain classes to the `com.actionbarsherlock.internal` package which + were not meant for public consumption. Despite being given `public` scope in + this new package, these classes should **NOT** be used under any circumstances + as their API can be considered highly volatile and is subject to change often + and without warning. + + +Version 3.0.1 *(2011-06-08)* +---------------------------- + + * Fix: `onOptionsItemSelected()` not being called in fragments if the activity + version returns `false`. + * Fix: `onCreateOptionsMenu()` not being called in fragments on Android 3.0+. + * New: Enable action item text display on pre-Android 3.0 by calling + `requestWindowFeature` with `Window.FEATURE_ENABLE_ACTION_BAR_WATSON_TEXT`. + * Fix: `setCustomView()` no longer automatically enables the custom view on + pre-3.0. You must call `setDisplayShowCustomEnabled()` in order to display + the view. + + +Version 3.0.0 *(2011-06-05)* +---------------------------- + +The API has been rewritten to mimic that of the native action bar. As a result, +usage now only requires changing a few imports to use the support versions +of classes and calling `getSupportActionBar()`. See the README for more info. + +The rewrite necessitated tight interaction with the +[compatibility library](http://android-developers.blogspot.com/2011/03/fragments-for-all.html) +to the point where its sources are now included. You are no longer required to +have the standalone `.jar` file. + +Also included is a default custom action bar for use by default on pre-3.0 +devices. This custom implementation is based off of Johan Nilsson's +[Android-ActionBar](https://github.com/johannilsson/android-actionbar) and the +[work that I have done](https://github.com/johannilsson/android-actionbar/pull/25) +on it. + +More details are available at http://actionbarsherlock.com + + +Version 2.1.1 *(2011-03-21)* +---------------------------- + +**No changes to library code.** + + * Moved library to the root of the repository. + * Added `samples/dependencies.py` script to automatically download the needed + dependencies for the sample projects. + + +Version 2.1.0 *(2011-03-21)* +---------------------------- + +**WARNING**: The +[Android Compatibility Library (v4)](http://android-developers.blogspot.com/2011/03/fragments-for-all.html) +is now required. + + * Added `ActionBarSherlock.Activity`, `ActionBarSherlock.FragmentActivity`, + and `ActionBarSherlock.ListActivity` for extension by implementing + activities, the latter of which is deprecated. This affords a much tighter + integration and allows for the use of other new features listed below. + * New API method: `layout(Fragment)` will use the fragment argument as the + content to the activity. + * New API method: `menu(int)` allows for the inflation of menu XMLs from a + resource. For the non-native implementation, the XML can be inflated to a + custom Menu which can then be applied appropriately to the third-party + action bar. Sub-menus are also supported. Third-party action bar handlers + should implement `ActionBarSherlock.HasMenu` for this functionality. *This + feature requires that activities extend from one of the provided activity + base classes.* + * New API method: `homeAsUp(boolean)`. This mimics the native method + `setDisplayHomeAsUpEnalbed` on the native action bar. Third-party action bar + handlers should implement `ActionBarSherlock.HasHomeAsUp` for this + functionality. + * New API method: `useLogo(boolean)` will trigger the action bar to hide the + application icon/home button and title and show a larger logo representing + the application. Third-party action bar handlers should implement + `ActionBarSherlock.HasLogo` for this functionality. + * New API method: `listNavigation(SpinnerAdapter, OnNavigationListener)`. Tells + the action bar to use drop-down style navigation with the specified list of + items and callback listener. Third-party action bar handlers should + implement `ActionBarSherlock.HasListNavigation` for this functionality. + * Javadocs are now available at + [jakewharton.github.com/ActionBarSherlock](http://jakewharton.github.com/ActionBarSherlock/). + * A standalone JAR is now available via the + [GitHub downloads page](https://github.com/JakeWharton/ActionBarSherlock/downloads) + or in my + [personal maven repository](http://r.jakewharton.com/maven/) + as `com.jakewharton:android-actionbarsherlock:2.1.0`. + + +Version 2.0.1 *(2011-03-11)* +---------------------------- + + * Use `Class.forName()` for detection of native action bar. This provides + compatability all the way back to Android 1.5. + + +Version 2.0.0 *(2011-03-09)* +---------------------------- +Complete rewrite! + + * New and better API. + * More sane logic and attachment to activity. + * Extensible via generics. Implement any ActionBar or roll your own with + minimal effort. + * Now a library project for easy inclusion in applications. + + +Version 1.0.0 *(2011-03-07)* +---------------------------- +Initial release. diff --git a/actionbarsherlock/LICENSE.txt b/actionbarsherlock/LICENSE.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/actionbarsherlock/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/actionbarsherlock/README.md b/actionbarsherlock/README.md new file mode 100644 index 000000000..cb13e9ebe --- /dev/null +++ b/actionbarsherlock/README.md @@ -0,0 +1,55 @@ +Action Bar Sherlock +=================== + +ActionBarSherlock is an extension of the [compatibility library][1] designed +to facilitate the use of the action bar design pattern across all versions of +Android through a single API. + +The library will automatically use the [native ActionBar][2] implementation on +Android 3.0 or later. For previous versions which do not include ActionBar, a +custom action bar implementation will automatically be wrapped around the +layout. Support for this goes all the way back to Android 1.6. + +Try out the sample applications on the Android Market: [Feature Demos][4], +[Shakespeare][5], and [Styled Action Bar][6]. + +**See http://actionbarsherlock.com for more information.** + +![Example Image][3] + + + +Developed By +============ + +* Jake Wharton - + + + +License +======= + + Copyright 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. + + + + + + [1]: http://android-developers.blogspot.com/2011/03/fragments-for-all.html + [2]: http://developer.android.com/guide/topics/ui/actionbar.html + [3]: http://actionbarsherlock.com/static/feature.png + [4]: https://market.android.com/details?id=com.actionbarsherlock.sample.demos + [5]: https://market.android.com/details?id=com.actionbarsherlock.sample.shakespeare + [6]: https://market.android.com/details?id=com.actionbarsherlock.sample.styledactionbar diff --git a/actionbarsherlock/library/AndroidManifest.xml b/actionbarsherlock/library/AndroidManifest.xml new file mode 100644 index 000000000..976015dd4 --- /dev/null +++ b/actionbarsherlock/library/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/actionbarsherlock/library/README.md b/actionbarsherlock/library/README.md new file mode 100644 index 000000000..4b22e6fe2 --- /dev/null +++ b/actionbarsherlock/library/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 +[download page][1]. + + + + + + + [1]: http://actionbarsherlock.com/download.html diff --git a/actionbarsherlock/library/bin/actionbarsherlock.jar b/actionbarsherlock/library/bin/actionbarsherlock.jar index 3dd76be0e..a13f9867d 100644 Binary files a/actionbarsherlock/library/bin/actionbarsherlock.jar and b/actionbarsherlock/library/bin/actionbarsherlock.jar differ diff --git a/actionbarsherlock/library/checkstyle.xml b/actionbarsherlock/library/checkstyle.xml new file mode 100644 index 000000000..91bcb9a52 --- /dev/null +++ b/actionbarsherlock/library/checkstyle.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/library/pom.xml b/actionbarsherlock/library/pom.xml new file mode 100644 index 000000000..79e1b29eb --- /dev/null +++ b/actionbarsherlock/library/pom.xml @@ -0,0 +1,104 @@ + + + + 4.0.0 + + com.actionbarsherlock + library + ActionBarSherlock + apklib + + + com.actionbarsherlock + parent + 3.4.2 + ../pom.xml + + + + + android + android + provided + + + + + src + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + + com.google.code.maven-replacer-plugin + maven-replacer-plugin + + + process-sources + + replace + + + + + false + target/generated-sources/r/com/actionbarsherlock/R.java + target/generated-sources/r/com/actionbarsherlock/R.java + false + static final int + static int + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + ${project.basedir}/checkstyle.xml + + + + verify + + checkstyle + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.7 + + + package + + attach-artifact + + + + + jar + ${project.build.directory}/${project.build.finalName}.jar + + + + + + + + + diff --git a/actionbarsherlock/library/project.properties b/actionbarsherlock/library/project.properties new file mode 100644 index 000000000..05e464d69 --- /dev/null +++ b/actionbarsherlock/library/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-13 diff --git a/actionbarsherlock/library/res/color/abs__item_bg.xml b/actionbarsherlock/library/res/color/abs__item_bg.xml new file mode 100644 index 000000000..1a5e2ed25 --- /dev/null +++ b/actionbarsherlock/library/res/color/abs__item_bg.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/actionbarsherlock/library/res/color/abs__tab_text_color_dark.xml b/actionbarsherlock/library/res/color/abs__tab_text_color_dark.xml new file mode 100644 index 000000000..ef120df86 --- /dev/null +++ b/actionbarsherlock/library/res/color/abs__tab_text_color_dark.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/library/res/color/abs__tab_text_color_light.xml b/actionbarsherlock/library/res/color/abs__tab_text_color_light.xml new file mode 100644 index 000000000..4174d1844 --- /dev/null +++ b/actionbarsherlock/library/res/color/abs__tab_text_color_light.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png new file mode 100644 index 000000000..1e3957222 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png new file mode 100644 index 000000000..a16db853e Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__cab_background_holo_dark.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__cab_background_holo_dark.9.png new file mode 100644 index 000000000..1a916b3fa Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__cab_background_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__cab_background_holo_light.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__cab_background_holo_light.9.png new file mode 100644 index 000000000..e8c9c6082 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__cab_background_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__cab_ic_close_focused_holo.png b/actionbarsherlock/library/res/drawable-hdpi/abs__cab_ic_close_focused_holo.png new file mode 100644 index 000000000..9d7f93208 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__cab_ic_close_focused_holo.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__cab_ic_close_normal_holo.png b/actionbarsherlock/library/res/drawable-hdpi/abs__cab_ic_close_normal_holo.png new file mode 100644 index 000000000..641507dfd Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__cab_ic_close_normal_holo.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__cab_ic_close_pressed_holo.png b/actionbarsherlock/library/res/drawable-hdpi/abs__cab_ic_close_pressed_holo.png new file mode 100644 index 000000000..ebb0fc825 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__cab_ic_close_pressed_holo.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png b/actionbarsherlock/library/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png new file mode 100644 index 000000000..7855cda9a Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__ic_ab_back_holo_light.png b/actionbarsherlock/library/res/drawable-hdpi/abs__ic_ab_back_holo_light.png new file mode 100644 index 000000000..c06277341 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__ic_ab_back_holo_light.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__list_focused_holo.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__list_focused_holo.9.png new file mode 100644 index 000000000..e354629ab Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__list_focused_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__list_longpressed_holo.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__list_longpressed_holo.9.png new file mode 100644 index 000000000..367c7d9ee Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__list_longpressed_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__list_pressed_holo_dark.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__list_pressed_holo_dark.9.png new file mode 100644 index 000000000..9eae8f41c Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__list_pressed_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__list_pressed_holo_light.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__list_pressed_holo_light.9.png new file mode 100644 index 000000000..a9131fcbc Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__list_pressed_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__list_selector_disabled_holo_dark.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__list_selector_disabled_holo_dark.9.png new file mode 100644 index 000000000..f6fd30dcd Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__list_selector_disabled_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__list_selector_disabled_holo_light.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__list_selector_disabled_holo_light.9.png new file mode 100644 index 000000000..ca8e9a277 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__list_selector_disabled_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_default_holo_dark.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_default_holo_dark.9.png new file mode 100644 index 000000000..ac36c068c Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_default_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_default_holo_light.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_default_holo_light.9.png new file mode 100644 index 000000000..68215998b Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_default_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_disabled_holo_dark.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_disabled_holo_dark.9.png new file mode 100644 index 000000000..adde69415 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_disabled_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_disabled_holo_light.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_disabled_holo_light.9.png new file mode 100644 index 000000000..fdb4bdff0 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_disabled_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_focused_holo_dark.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_focused_holo_dark.9.png new file mode 100644 index 000000000..d40d165b7 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_focused_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_focused_holo_light.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_focused_holo_light.9.png new file mode 100644 index 000000000..096b977f3 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_focused_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_pressed_holo_dark.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_pressed_holo_dark.9.png new file mode 100644 index 000000000..3b260170c Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_pressed_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_pressed_holo_light.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_pressed_holo_light.9.png new file mode 100644 index 000000000..33b661b07 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__spinner_pressed_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png new file mode 100644 index 000000000..1ba35d554 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__tab_selected_holo.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_selected_holo.9.png new file mode 100644 index 000000000..ef913cc8e Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_selected_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__tab_selected_pressed_focused_holo.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_selected_pressed_focused_holo.9.png new file mode 100644 index 000000000..d7e968806 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_selected_pressed_focused_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__tab_selected_pressed_holo.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_selected_pressed_holo.9.png new file mode 100644 index 000000000..b8b1fcfe2 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_selected_pressed_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__tab_unselected_focused_holo.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_unselected_focused_holo.9.png new file mode 100644 index 000000000..256e8e730 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_unselected_focused_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__tab_unselected_holo.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_unselected_holo.9.png new file mode 100644 index 000000000..eaa306aee Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_unselected_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__tab_unselected_pressed_focused_holo.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_unselected_pressed_focused_holo.9.png new file mode 100644 index 000000000..d17b82026 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_unselected_pressed_focused_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-hdpi/abs__tab_unselected_pressed_holo.9.png b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_unselected_pressed_holo.9.png new file mode 100644 index 000000000..a344994fb Binary files /dev/null and b/actionbarsherlock/library/res/drawable-hdpi/abs__tab_unselected_pressed_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png new file mode 100644 index 000000000..0ad6c888b Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png new file mode 100644 index 000000000..19b50abcb Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__cab_background_holo_dark.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__cab_background_holo_dark.9.png new file mode 100644 index 000000000..38f44ce1c Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__cab_background_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__cab_background_holo_light.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__cab_background_holo_light.9.png new file mode 100644 index 000000000..2a4deea44 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__cab_background_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__cab_ic_close_focused_holo.png b/actionbarsherlock/library/res/drawable-mdpi/abs__cab_ic_close_focused_holo.png new file mode 100644 index 000000000..59149e4a5 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__cab_ic_close_focused_holo.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__cab_ic_close_normal_holo.png b/actionbarsherlock/library/res/drawable-mdpi/abs__cab_ic_close_normal_holo.png new file mode 100644 index 000000000..b37888533 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__cab_ic_close_normal_holo.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__cab_ic_close_pressed_holo.png b/actionbarsherlock/library/res/drawable-mdpi/abs__cab_ic_close_pressed_holo.png new file mode 100644 index 000000000..29b040780 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__cab_ic_close_pressed_holo.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png b/actionbarsherlock/library/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png new file mode 100644 index 000000000..ae3e6bfb8 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__ic_ab_back_holo_light.png b/actionbarsherlock/library/res/drawable-mdpi/abs__ic_ab_back_holo_light.png new file mode 100644 index 000000000..c61e3fa86 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__ic_ab_back_holo_light.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__list_focused_holo.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__list_focused_holo.9.png new file mode 100644 index 000000000..e2449ddd5 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__list_focused_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__list_longpressed_holo.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__list_longpressed_holo.9.png new file mode 100644 index 000000000..3483891c5 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__list_longpressed_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png new file mode 100644 index 000000000..e3344b627 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__list_pressed_holo_light.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__list_pressed_holo_light.9.png new file mode 100644 index 000000000..236597868 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__list_pressed_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__list_selector_disabled_holo_dark.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__list_selector_disabled_holo_dark.9.png new file mode 100644 index 000000000..92da2f0dd Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__list_selector_disabled_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__list_selector_disabled_holo_light.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__list_selector_disabled_holo_light.9.png new file mode 100644 index 000000000..42cb6463e Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__list_selector_disabled_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_default_holo_dark.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_default_holo_dark.9.png new file mode 100644 index 000000000..99b16056b Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_default_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_default_holo_light.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_default_holo_light.9.png new file mode 100644 index 000000000..c820e40a1 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_default_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_disabled_holo_dark.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_disabled_holo_dark.9.png new file mode 100644 index 000000000..95d7c86e5 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_disabled_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_disabled_holo_light.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_disabled_holo_light.9.png new file mode 100644 index 000000000..2dba2704f Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_disabled_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_focused_holo_dark.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_focused_holo_dark.9.png new file mode 100644 index 000000000..2e2fadd83 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_focused_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_focused_holo_light.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_focused_holo_light.9.png new file mode 100644 index 000000000..5bbbf6395 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_focused_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_pressed_holo_dark.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_pressed_holo_dark.9.png new file mode 100644 index 000000000..083194a6f Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_pressed_holo_dark.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_pressed_holo_light.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_pressed_holo_light.9.png new file mode 100644 index 000000000..29cbc4621 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__spinner_pressed_holo_light.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png new file mode 100644 index 000000000..9a33cd2d9 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__tab_selected_holo.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_selected_holo.9.png new file mode 100644 index 000000000..e029e577a Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_selected_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__tab_selected_pressed_focused_holo.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_selected_pressed_focused_holo.9.png new file mode 100644 index 000000000..285116e31 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_selected_pressed_focused_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__tab_selected_pressed_holo.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_selected_pressed_holo.9.png new file mode 100644 index 000000000..dadefa758 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_selected_pressed_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__tab_unselected_focused_holo.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_unselected_focused_holo.9.png new file mode 100644 index 000000000..032a9921c Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_unselected_focused_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__tab_unselected_holo.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_unselected_holo.9.png new file mode 100644 index 000000000..848f3f133 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_unselected_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__tab_unselected_pressed_focused_holo.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_unselected_pressed_focused_holo.9.png new file mode 100644 index 000000000..384513584 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_unselected_pressed_focused_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-mdpi/abs__tab_unselected_pressed_holo.9.png b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_unselected_pressed_holo.9.png new file mode 100644 index 000000000..23fd8c9b3 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-mdpi/abs__tab_unselected_pressed_holo.9.png differ diff --git a/actionbarsherlock/library/res/drawable-nodpi-v11/abs__action_item_divider.9.png b/actionbarsherlock/library/res/drawable-nodpi-v11/abs__action_item_divider.9.png new file mode 100644 index 000000000..c30f607d0 Binary files /dev/null and b/actionbarsherlock/library/res/drawable-nodpi-v11/abs__action_item_divider.9.png differ diff --git a/actionbarsherlock/library/res/drawable-nodpi/abs__action_item_divider.9.png b/actionbarsherlock/library/res/drawable-nodpi/abs__action_item_divider.9.png new file mode 100644 index 000000000..62332880b Binary files /dev/null and b/actionbarsherlock/library/res/drawable-nodpi/abs__action_item_divider.9.png differ diff --git a/actionbarsherlock/library/res/drawable/abs__cab_ic_close_holo.xml b/actionbarsherlock/library/res/drawable/abs__cab_ic_close_holo.xml new file mode 100644 index 000000000..0da7e2370 --- /dev/null +++ b/actionbarsherlock/library/res/drawable/abs__cab_ic_close_holo.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/actionbarsherlock/library/res/drawable/abs__item_background_holo_dark.xml b/actionbarsherlock/library/res/drawable/abs__item_background_holo_dark.xml new file mode 100644 index 000000000..b7b97b5ae --- /dev/null +++ b/actionbarsherlock/library/res/drawable/abs__item_background_holo_dark.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/library/res/drawable/abs__item_background_holo_light.xml b/actionbarsherlock/library/res/drawable/abs__item_background_holo_light.xml new file mode 100644 index 000000000..fdcac1d8a --- /dev/null +++ b/actionbarsherlock/library/res/drawable/abs__item_background_holo_light.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/library/res/drawable/abs__list_selector_background_transition_holo_dark.xml b/actionbarsherlock/library/res/drawable/abs__list_selector_background_transition_holo_dark.xml new file mode 100644 index 000000000..b2ce4f0f7 --- /dev/null +++ b/actionbarsherlock/library/res/drawable/abs__list_selector_background_transition_holo_dark.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/actionbarsherlock/library/res/drawable/abs__list_selector_background_transition_holo_light.xml b/actionbarsherlock/library/res/drawable/abs__list_selector_background_transition_holo_light.xml new file mode 100644 index 000000000..d7e31b1d1 --- /dev/null +++ b/actionbarsherlock/library/res/drawable/abs__list_selector_background_transition_holo_light.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/actionbarsherlock/library/res/drawable/abs__spinner_background_holo_dark.xml b/actionbarsherlock/library/res/drawable/abs__spinner_background_holo_dark.xml new file mode 100644 index 000000000..8d3500404 --- /dev/null +++ b/actionbarsherlock/library/res/drawable/abs__spinner_background_holo_dark.xml @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/actionbarsherlock/library/res/drawable/abs__spinner_background_holo_light.xml b/actionbarsherlock/library/res/drawable/abs__spinner_background_holo_light.xml new file mode 100644 index 000000000..0770f1093 --- /dev/null +++ b/actionbarsherlock/library/res/drawable/abs__spinner_background_holo_light.xml @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/actionbarsherlock/library/res/drawable/abs__tab_indicator_holo.xml b/actionbarsherlock/library/res/drawable/abs__tab_indicator_holo.xml new file mode 100644 index 000000000..542a5cee3 --- /dev/null +++ b/actionbarsherlock/library/res/drawable/abs__tab_indicator_holo.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/library/res/layout-land/abs__action_bar.xml b/actionbarsherlock/library/res/layout-land/abs__action_bar.xml new file mode 100644 index 000000000..a9641546e --- /dev/null +++ b/actionbarsherlock/library/res/layout-land/abs__action_bar.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/actionbarsherlock/library/res/layout-land/abs__screen_action_bar.xml b/actionbarsherlock/library/res/layout-land/abs__screen_action_bar.xml new file mode 100644 index 000000000..794f52831 --- /dev/null +++ b/actionbarsherlock/library/res/layout-land/abs__screen_action_bar.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/actionbarsherlock/library/res/layout-land/abs__screen_action_bar_overlay.xml b/actionbarsherlock/library/res/layout-land/abs__screen_action_bar_overlay.xml new file mode 100644 index 000000000..66a63f9dd --- /dev/null +++ b/actionbarsherlock/library/res/layout-land/abs__screen_action_bar_overlay.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/actionbarsherlock/library/res/layout-large/abs__action_bar.xml b/actionbarsherlock/library/res/layout-large/abs__action_bar.xml new file mode 100644 index 000000000..a9641546e --- /dev/null +++ b/actionbarsherlock/library/res/layout-large/abs__action_bar.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/actionbarsherlock/library/res/layout-large/abs__screen_action_bar.xml b/actionbarsherlock/library/res/layout-large/abs__screen_action_bar.xml new file mode 100644 index 000000000..794f52831 --- /dev/null +++ b/actionbarsherlock/library/res/layout-large/abs__screen_action_bar.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/actionbarsherlock/library/res/layout-large/abs__screen_action_bar_overlay.xml b/actionbarsherlock/library/res/layout-large/abs__screen_action_bar_overlay.xml new file mode 100644 index 000000000..66a63f9dd --- /dev/null +++ b/actionbarsherlock/library/res/layout-large/abs__screen_action_bar_overlay.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/actionbarsherlock/library/res/layout/abs__action_bar.xml b/actionbarsherlock/library/res/layout/abs__action_bar.xml new file mode 100644 index 000000000..4e3827f33 --- /dev/null +++ b/actionbarsherlock/library/res/layout/abs__action_bar.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/library/res/layout/abs__action_bar_home.xml b/actionbarsherlock/library/res/layout/abs__action_bar_home.xml new file mode 100644 index 000000000..5bff71ddc --- /dev/null +++ b/actionbarsherlock/library/res/layout/abs__action_bar_home.xml @@ -0,0 +1,38 @@ + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/library/res/layout/abs__action_bar_inline.xml b/actionbarsherlock/library/res/layout/abs__action_bar_inline.xml new file mode 100644 index 000000000..9aa245b29 --- /dev/null +++ b/actionbarsherlock/library/res/layout/abs__action_bar_inline.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/library/res/layout/abs__action_bar_item_layout.xml b/actionbarsherlock/library/res/layout/abs__action_bar_item_layout.xml new file mode 100644 index 000000000..85d866175 --- /dev/null +++ b/actionbarsherlock/library/res/layout/abs__action_bar_item_layout.xml @@ -0,0 +1,52 @@ + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/library/res/layout/abs__action_bar_tab_layout.xml b/actionbarsherlock/library/res/layout/abs__action_bar_tab_layout.xml new file mode 100644 index 000000000..8af6662eb --- /dev/null +++ b/actionbarsherlock/library/res/layout/abs__action_bar_tab_layout.xml @@ -0,0 +1,53 @@ + + + + + + + diff --git a/actionbarsherlock/library/res/layout/abs__action_bar_title_item.xml b/actionbarsherlock/library/res/layout/abs__action_bar_title_item.xml new file mode 100644 index 000000000..1d37ef58c --- /dev/null +++ b/actionbarsherlock/library/res/layout/abs__action_bar_title_item.xml @@ -0,0 +1,44 @@ + + + + + + + diff --git a/actionbarsherlock/library/res/layout/abs__screen_action_bar.xml b/actionbarsherlock/library/res/layout/abs__screen_action_bar.xml new file mode 100644 index 000000000..d3a5fb796 --- /dev/null +++ b/actionbarsherlock/library/res/layout/abs__screen_action_bar.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/library/res/layout/abs__screen_action_bar_inline.xml b/actionbarsherlock/library/res/layout/abs__screen_action_bar_inline.xml new file mode 100644 index 000000000..16c05d11b --- /dev/null +++ b/actionbarsherlock/library/res/layout/abs__screen_action_bar_inline.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/library/res/layout/abs__screen_action_bar_inline_overlay.xml b/actionbarsherlock/library/res/layout/abs__screen_action_bar_inline_overlay.xml new file mode 100644 index 000000000..a8243dd47 --- /dev/null +++ b/actionbarsherlock/library/res/layout/abs__screen_action_bar_inline_overlay.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/library/res/layout/abs__screen_action_bar_overlay.xml b/actionbarsherlock/library/res/layout/abs__screen_action_bar_overlay.xml new file mode 100644 index 000000000..a9c3cc7a0 --- /dev/null +++ b/actionbarsherlock/library/res/layout/abs__screen_action_bar_overlay.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/library/res/layout/abs__screen_simple.xml b/actionbarsherlock/library/res/layout/abs__screen_simple.xml new file mode 100644 index 000000000..0687578fb --- /dev/null +++ b/actionbarsherlock/library/res/layout/abs__screen_simple.xml @@ -0,0 +1,43 @@ + + + + + + + diff --git a/actionbarsherlock/library/res/layout/abs__simple_spinner_item.xml b/actionbarsherlock/library/res/layout/abs__simple_spinner_item.xml new file mode 100644 index 000000000..9be5ea8d1 --- /dev/null +++ b/actionbarsherlock/library/res/layout/abs__simple_spinner_item.xml @@ -0,0 +1,26 @@ + + + diff --git a/actionbarsherlock/library/res/values-land/abs__constants.xml b/actionbarsherlock/library/res/values-land/abs__constants.xml new file mode 100644 index 000000000..c9028db7c --- /dev/null +++ b/actionbarsherlock/library/res/values-land/abs__constants.xml @@ -0,0 +1,5 @@ + + + + 4 + \ No newline at end of file diff --git a/actionbarsherlock/library/res/values-v11/abs__styles.xml b/actionbarsherlock/library/res/values-v11/abs__styles.xml new file mode 100644 index 000000000..5c0dd1036 --- /dev/null +++ b/actionbarsherlock/library/res/values-v11/abs__styles.xml @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/library/res/values-v13/abs__styles.xml b/actionbarsherlock/library/res/values-v13/abs__styles.xml new file mode 100644 index 000000000..4112a4aad --- /dev/null +++ b/actionbarsherlock/library/res/values-v13/abs__styles.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/actionbarsherlock/library/res/values/abs__attrs.xml b/actionbarsherlock/library/res/values/abs__attrs.xml new file mode 100644 index 000000000..1279c78e5 --- /dev/null +++ b/actionbarsherlock/library/res/values/abs__attrs.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/library/res/values/abs__constants.xml b/actionbarsherlock/library/res/values/abs__constants.xml new file mode 100644 index 000000000..ddcb58b97 --- /dev/null +++ b/actionbarsherlock/library/res/values/abs__constants.xml @@ -0,0 +1,7 @@ + + + + #FF6899FF + 3 + tabUnderAb + \ No newline at end of file diff --git a/actionbarsherlock/library/res/values/abs__styles.xml b/actionbarsherlock/library/res/values/abs__styles.xml new file mode 100644 index 000000000..1b01d5093 --- /dev/null +++ b/actionbarsherlock/library/res/values/abs__styles.xml @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/library/src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java b/actionbarsherlock/library/src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java new file mode 100644 index 000000000..4be290999 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java @@ -0,0 +1,221 @@ +/* + * 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 android.support.v4.accessibilityservice; + +//import android.accessibilityservice.AccessibilityService; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.pm.ResolveInfo; + +/** + * Helper for accessing newer features in AccessibilityServiceInfo. + */ +public class AccessibilityServiceInfoCompat { + + static interface AccessibilityServiceInfoVersionImpl { + public String getId(AccessibilityServiceInfo info); + public ResolveInfo getResolveInfo(AccessibilityServiceInfo info); + public boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info); + public String getDescription(AccessibilityServiceInfo info); + public String getSettingsActivityName(AccessibilityServiceInfo info); + } + + static class AccessibilityServiceInfoStubImpl implements AccessibilityServiceInfoVersionImpl { + + public boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) { + return false; + } + + public String getDescription(AccessibilityServiceInfo info) { + return null; + } + + public String getId(AccessibilityServiceInfo info) { + return null; + } + + public ResolveInfo getResolveInfo(AccessibilityServiceInfo info) { + return null; + } + + public String getSettingsActivityName(AccessibilityServiceInfo info) { + return null; + } + } +/* + static class AccessibilityServiceInfoIcsImpl extends AccessibilityServiceInfoStubImpl { + + @Override + public boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) { + return AccessibilityServiceInfoCompatIcs.getCanRetrieveWindowContent(info); + } + + @Override + public String getDescription(AccessibilityServiceInfo info) { + return AccessibilityServiceInfoCompatIcs.getDescription(info); + } + + @Override + public String getId(AccessibilityServiceInfo info) { + return AccessibilityServiceInfoCompatIcs.getId(info); + } + + @Override + public ResolveInfo getResolveInfo(AccessibilityServiceInfo info) { + return AccessibilityServiceInfoCompatIcs.getResolveInfo(info); + } + + @Override + public String getSettingsActivityName(AccessibilityServiceInfo info) { + return AccessibilityServiceInfoCompatIcs.getSettingsActivityName(info); + } + } +*/ + static { + //if (Build.VERSION.SDK_INT >= 14) { // ICS + // IMPL = new AccessibilityServiceInfoIcsImpl(); + //} else { + IMPL = new AccessibilityServiceInfoStubImpl(); + //} + } + + private static final AccessibilityServiceInfoVersionImpl IMPL; + + /** + * Mask for all feedback types. + * + * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN + * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC + * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE + * @see AccessibilityServiceInfo#FEEDBACK_VISUAL + * @see AccessibilityServiceInfo#FEEDBACK_GENERIC + */ + public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF; + + /* + * Hide constructor + */ + private AccessibilityServiceInfoCompat() { + + } + + /** + * The accessibility service id. + *

+ * Generated by the system. + *

+ * + * @return The id. + */ + public static String getId(AccessibilityServiceInfo info) { + return IMPL.getId(info); + } + + /** + * The service {@link ResolveInfo}. + *

+ * Generated by the system. + *

+ * + * @return The info. + */ + public static ResolveInfo getResolveInfo(AccessibilityServiceInfo info) { + return IMPL.getResolveInfo(info); + } + + /** + * The settings activity name. + *

+ * Statically set from {@link AccessibilityService#SERVICE_META_DATA + * meta-data}. + *

+ * + * @return The settings activity name. + */ + public static String getSettingsActivityName(AccessibilityServiceInfo info) { + return IMPL.getSettingsActivityName(info); + } + + /** + * Whether this service can retrieve the current window's content. + *

+ * Statically set from {@link AccessibilityService#SERVICE_META_DATA + * meta-data}. + *

+ * + * @return True window content can be retrieved. + */ + public static boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) { + return IMPL.getCanRetrieveWindowContent(info); + } + + /** + * Description of the accessibility service. + *

+ * Statically set from {@link AccessibilityService#SERVICE_META_DATA + * meta-data}. + *

+ * + * @return The description. + */ + public static String getDescription(AccessibilityServiceInfo info) { + return IMPL.getDescription(info); + } + + /** + * Returns the string representation of a feedback type. For example, + * {@link AccessibilityServiceInfo#FEEDBACK_SPOKEN} is represented by the + * string FEEDBACK_SPOKEN. + * + * @param feedbackType The feedback type. + * @return The string representation. + */ + public static String feedbackTypeToString(int feedbackType) { + switch (feedbackType) { + case AccessibilityServiceInfo.FEEDBACK_AUDIBLE: + return "FEEDBACK_AUDIBLE"; + case AccessibilityServiceInfo.FEEDBACK_HAPTIC: + return "FEEDBACK_HAPTIC"; + case AccessibilityServiceInfo.FEEDBACK_GENERIC: + return "FEEDBACK_GENERIC"; + case AccessibilityServiceInfo.FEEDBACK_SPOKEN: + return "FEEDBACK_SPOKEN"; + case AccessibilityServiceInfo.FEEDBACK_VISUAL: + return "FEEDBACK_VISUAL"; + case AccessibilityServiceInfoCompat.FEEDBACK_ALL_MASK: + return "FEEDBACK_ALL"; + default: + return null; + } + } + + /** + * Returns the string representation of a flag. For example, + * {@link AccessibilityServiceInfo#DEFAULT} is represented by the + * string DEFAULT. + * + * @param flag The flag. + * @return The string representation. + */ + public static String flagToString(int flag) { + switch (flag) { + case AccessibilityServiceInfo.DEFAULT: + return "DEFAULT"; + default: + return null; + } + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java b/actionbarsherlock/library/src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java new file mode 100644 index 000000000..c5be82b0e --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java @@ -0,0 +1,48 @@ +/* + * 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 android.support.v4.accessibilityservice; + +//import android.accessibilityservice.AccessibilityServiceInfo; +//import android.content.pm.ResolveInfo; + +/** + * ICS implementation of the new APIs in AccessibilityServiceInfo. + */ +class AccessibilityServiceInfoCompatIcs { +/* + public static boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) { + return info.getCanRetrieveWindowContent(); + } + + public static String getDescription(AccessibilityServiceInfo info) { + return info.getDescription(); + } + + public static String getId(AccessibilityServiceInfo info) { + return info.getId(); + } + + public static ResolveInfo getResolveInfo(AccessibilityServiceInfo info) { + return info.getResolveInfo(); + } + + public static String getSettingsActivityName(AccessibilityServiceInfo info) { + return info.getSettingsActivityName(); + } +*/ +} diff --git a/actionbarsherlock/library/src/android/support/v4/app/ActionBar.java b/actionbarsherlock/library/src/android/support/v4/app/ActionBar.java new file mode 100644 index 000000000..1d7d9b376 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/ActionBar.java @@ -0,0 +1,795 @@ +/* + * Copyright 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 android.support.v4.app; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.v4.view.ActionMode; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.MarginLayoutParams; +import android.widget.SpinnerAdapter; + +/** + * This is the public interface to the contextual ActionBar. The ActionBar acts + * as a replacement for the title bar in Activities. It provides facilities for + * creating toolbar actions as well as methods of navigating around an + * application. + */ +public abstract class ActionBar { + /** + * Return the actual public action bar instance. This will either return + * itself or null depending on the state of the underlying action bar. + * + * @return Action bar instance. + */ + protected abstract ActionBar getPublicInstance(); + + // ------------------------------------------------------------------------ + // ACTION MODE SUPPORT + // ------------------------------------------------------------------------ + + protected abstract ActionMode startActionMode(ActionMode.Callback callback); + + // ------------------------------------------------------------------------ + // ACTION BAR SUPPORT + // ------------------------------------------------------------------------ + + /** + * Per-child layout information associated with action bar custom views. + */ + public static class LayoutParams extends MarginLayoutParams { + /** + * Gravity for the view associated with these LayoutParams. + * + * @see android.view.Gravity + */ + public int gravity = -1; + + public LayoutParams() { + this(-1); + } + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + public LayoutParams(int width, int height) { + super(width, height); + } + public LayoutParams(int width, int height, int gravity) { + this(width, height); + this.gravity = gravity; + } + public LayoutParams(int gravity) { + this(0, 0, gravity); + } + public LayoutParams(LayoutParams source) { + super(source); + this.gravity = source.gravity; + } + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + } + + /** + * 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. + */ + void onMenuVisibilityChanged(boolean isVisible); + } + + /** + * 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. + */ + boolean onNavigationItemSelected(int itemPosition, long itemId); + } + + /** + *

A tab in the action bar.

+ * + *

Tabs manage the hiding and showing of + * {@link android.support.v4.app.Fragment}.

+ */ + public static abstract class Tab { + /** + * An invalid position for a tab. + * + * @see #getPosition() + */ + public static int INVALID_POSITION = android.app.ActionBar.Tab.INVALID_POSITION; + + + /** + * Retrieve a previously set custom view for this tab. + * + * @return The custom view set by {@link #setCustomView(View)}. + */ + public abstract View getCustomView(); + + /** + * Return the icon associated with this tab. + * + * @return The tab's icon + */ + public abstract Drawable getIcon(); + + /** + * 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 current tab listener. + * + * @return Tab listener. + */ + public abstract ActionBar.TabListener getTabListener(); + + /** + * @return This Tab's tag object. + */ + public abstract Object getTag(); + + /** + * Return the text of this tab. + * + * @return The tab's text + */ + public abstract CharSequence getText(); + + /** + * Select this tab. Only valid if the tab has been added to the action + * bar. + */ + public abstract void select(); + + /** + * 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 ActionBar.Tab setCustomView(int layoutResId); + + /** + * 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 ActionBar.Tab setCustomView(View view); + + /** + * 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 ActionBar.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 ActionBar.Tab setIcon(int resId); + + /** + * Set the {@link ActionBar.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 ActionBar.Tab setTabListener(ActionBar.TabListener listener); + + /** + * 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 ActionBar.Tab setTag(Object obj); + + /** + * 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 ActionBar.Tab setText(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 ActionBar.Tab setText(CharSequence text); + } + + /** + * Callback interface invoked when a tab is focused, unfocused, added, or + * removed. + */ + public interface TabListener { + /** + * 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 Unused, always {@code null}. Begin your own transaction by + * calling {@link FragmentActivity#getSupportFragmentManager()}. + */ + void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft); + + /** + * Called when a tab enters the selected state. + * + * @param tab The tab that was selected + * @param ft Unused, always {@code null}. Begin your own transaction by + * calling {@link FragmentActivity#getSupportFragmentManager()}. + */ + void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft); + + /** + * Called when a tab exits the selected state. + * + * @param tab The tab that was unselected + * @param ft Unused, always {@code null}. Begin your own transaction by + * calling {@link FragmentActivity#getSupportFragmentManager()}. + */ + void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft); + } + + + + /** + * 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. + * + * @see #setDisplayOptions(int) + * @see #setDisplayOptions(int, int) + */ + public static final int DISPLAY_HOME_AS_UP = android.app.ActionBar.DISPLAY_HOME_AS_UP; + + /** + * Show the custom view if one has been set. + * + * @see #setCustomView(int) + * @see #setCustomView(View) + * @see #setCustomView(View, LayoutParams) + * @see #setDisplayOptions(int) + * @see #setDisplayOptions(int, int) + */ + public static final int DISPLAY_SHOW_CUSTOM = android.app.ActionBar.DISPLAY_SHOW_CUSTOM; + + /** + * 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; + + /** + * 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; + + /** + * 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; + + /** + * 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; + + /** + * 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; + + /** + * 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; + + + + /** + * Add a listener that will respond to menu visibility change events. + * + * @param listener The new listener to add + */ + public abstract void addOnMenuVisibilityListener(ActionBar.OnMenuVisibilityListener listener); + + /** + * 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(ActionBar.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(ActionBar.Tab tab, boolean setSelected); + + /** + * Add a tab for use in tabbed navigation mode. The tab will be inserted at + * {@code position}. 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(ActionBar.Tab tab, int position); + + /** + * Add a tab for use in tabbed navigation mode. The tab will be insterted at + * {@code position}. + * + * @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(ActionBar.Tab tab, int position, boolean setSelected); + + /** + * @return The current custom view. + */ + public abstract View getCustomView(); + + /** + * @return The current set of display options. + */ + public abstract int getDisplayOptions(); + + /** + * Retrieve the current height of the ActionBar. + * + * @return The ActionBar's height + */ + public abstract int getHeight(); + + /** + * Get the number of navigation items present in the current navigation + * mode. + * + * @return Number of navigation items. + */ + public abstract int getNavigationItemCount(); + + /** + * Returns the current navigation mode. The result will be one of: + *
    + *
  • {@link #NAVIGATION_MODE_STANDARD}
  • + *
  • {@link #NAVIGATION_MODE_LIST}
  • + *
  • {@link #NAVIGATION_MODE_TABS}
  • + *
+ * + * @return The current navigation mode. + * @see #setNavigationMode(int) + */ + public abstract int getNavigationMode(); + + /** + * Get the position of the selected navigation item in list or tabbed + * navigation modes. + * + * @return Position of the selected item. + */ + public abstract int getSelectedNavigationIndex(); + + /** + * 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 ActionBar.Tab getSelectedTab(); + + /** + * 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 tab at the specified index. + * + * @param index Index value in the range 0-get + * @return Tab at specified index + */ + public abstract ActionBar.Tab getTabAt(int index); + + /** + * Returns the number of tabs currently registered with the action bar. + * + * @return Tab count + */ + public abstract int getTabCount(); + + /** + * 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(); + + /** + * Hide the ActionBar if it is not currently showing. If the window hosting + * the ActionBar does not have the feature + * {@link android.support.v4.view.Window#FEATURE_ACTION_BAR_OVERLAY} + * it will resize application content to fit the new space available. + */ + public abstract void hide(); + + /** + * @return {@code true} if the ActionBar is showing, {@code false} + * otherwise. + */ + public abstract boolean isShowing(); + + /** + * Create and return a new ActionBar.Tab. This tab will not be included in + * the action bar until it is added. + * + * @return A new Tab + * @see #addTab(Tab) + * @see #addTab(Tab, boolean) + * @see #addTab(Tab, int) + * @see #addTab(Tab, int, boolean) + */ + public abstract ActionBar.Tab newTab(); + + /** + * Remove all tabs from the action bar and deselect the current tab. + */ + public abstract void removeAllTabs(); + + /** + * 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(ActionBar.OnMenuVisibilityListener listener); + + /** + * 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(ActionBar.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); + + /** + *

Select the specified tab. If it is not a child of this action bar it + * will be added.

+ * + *

Note: If you want to select by index, use + * {@link #setSelectedNavigationItem(int)}.

+ * + * @param tab Tab to select + */ + public abstract void selectTab(ActionBar.Tab tab); + + /** + * Set the ActionBar's background. + * + * @param d Background drawable + */ + public abstract void setBackgroundDrawable(Drawable d); + + /** + *

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.

+ * + *

The display option {@link #DISPLAY_SHOW_CUSTOM} must be set for the + * custom view to be displayed.

+ * + * @param resId Resource ID of a layout to inflate into the ActionBar. + * @see #setDisplayOptions(int, int) + */ + public abstract void setCustomView(int resId); + + /** + * 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.

+ * + *

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.

+ * + *

The display option {@link #DISPLAY_SHOW_CUSTOM} must be set for the + * custom view to be displayed.

+ * + * @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, ActionBar.LayoutParams layoutParams); + + /** + *

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.

+ * + *

To set several display options at once, see the setDisplayOptions + * methods.

+ * + * @param showHomeAsUp {@code 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 selected display options. Only the options specified by mask will + * be changed. To change all display option bits at once, see + * {@link #setDisplayOptions(int)}. + * + *

Example: {@code setDisplayOptions(0, DISPLAY_SHOW_HOME)} will disable + * the {@link #DISPLAY_SHOW_HOME} option. + * {@code 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 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 whether a custom view should be displayed, if set.

+ * + *

To set several display options at once, see the setDisplayOptions + * methods.

+ * + * @param showCustom {@code true} if the currently set custom view should be + * displayed, {@code false} otherwise. + * @see #setDisplayOptions(int) + * @see #setDisplayOptions(int, int) + */ + public abstract void setDisplayShowCustomEnabled(boolean showCustom); + + /** + *

Set whether to include the application home affordance in the action + * bar. Home is presented as either an activity icon or logo.

+ * + *

To set several display options at once, see the setDisplayOptions + * methods.

+ * + * @param showHome {@code true} to show home, {@code false} otherwise. + * @see #setDisplayOptions(int) + * @see #setDisplayOptions(int, int) + */ + public abstract void setDisplayShowHomeEnabled(boolean showHome); + + /** + *

Set whether an activity title/subtitle should be displayed.

+ * + *

To set several display options at once, see the setDisplayOptions + * methods.

+ * + * @param showTitle {@code true} to display a title/subtitle if present. + * @see #setDisplayOptions(int) + * @see #setDisplayOptions(int, int) + */ + public abstract void setDisplayShowTitleEnabled(boolean showTitle); + + /** + *

Set whether to display the activity logo rather than the activity + * icon. A logo is often a wider, more detailed image.

+ * + *

To set several display options at once, see the setDisplayOptions + * methods.

+ * + * @param useLogo {@code true} to use the activity logo, {@code false} to + * use the activity icon. + * @see #setDisplayOptions(int) + * @see #setDisplayOptions(int, int) + */ + public abstract void setDisplayUseLogoEnabled(boolean useLogo); + + /** + * 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, ActionBar.OnNavigationListener callback); + + /** + * 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); + + /** + * 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); + + /** + * 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 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 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); + + /** + * Show the ActionBar if it is not currently showing. If the window hosting + * the ActionBar does not have the feature + * {@link android.support.v4.view.Window#FEATURE_ACTION_BAR_OVERLAY} + * it will resize application content to fit the new space available. + */ + public abstract void show(); +} \ No newline at end of file diff --git a/actionbarsherlock/library/src/android/support/v4/app/BackStackRecord.java b/actionbarsherlock/library/src/android/support/v4/app/BackStackRecord.java new file mode 100644 index 000000000..bebd010cb --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/BackStackRecord.java @@ -0,0 +1,731 @@ +/* + * 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 android.support.v4.app; + +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.Log; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import com.actionbarsherlock.R; + +final class BackStackState implements Parcelable { + final int[] mOps; + final int mTransition; + final int mTransitionStyle; + final String mName; + final int mIndex; + final int mBreadCrumbTitleRes; + final CharSequence mBreadCrumbTitleText; + final int mBreadCrumbShortTitleRes; + final CharSequence mBreadCrumbShortTitleText; + + public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) { + int numRemoved = 0; + BackStackRecord.Op op = bse.mHead; + while (op != null) { + if (op.removed != null) numRemoved += op.removed.size(); + op = op.next; + } + mOps = new int[bse.mNumOp*7 + numRemoved]; + + if (!bse.mAddToBackStack) { + throw new IllegalStateException("Not on back stack"); + } + + op = bse.mHead; + int pos = 0; + while (op != null) { + mOps[pos++] = op.cmd; + mOps[pos++] = op.fragment.mIndex; + mOps[pos++] = op.enterAnim; + mOps[pos++] = op.exitAnim; + mOps[pos++] = op.popEnterAnim; + mOps[pos++] = op.popExitAnim; + if (op.removed != null) { + final int N = op.removed.size(); + mOps[pos++] = N; + for (int i=0; i 0) { + op.removed = new ArrayList(N); + for (int i=0; i CREATOR + = new Parcelable.Creator() { + public BackStackState createFromParcel(Parcel in) { + return new BackStackState(in); + } + + public BackStackState[] newArray(int size) { + return new BackStackState[size]; + } + }; +} + +/** + * @hide Entry of an operation on the fragment back stack. + */ +final class BackStackRecord extends FragmentTransaction implements + FragmentManager.BackStackEntry, Runnable { + static final String TAG = "BackStackEntry"; + + private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; + + final FragmentManagerImpl mManager; + + static final int OP_NULL = 0; + static final int OP_ADD = 1; + static final int OP_REPLACE = 2; + static final int OP_REMOVE = 3; + static final int OP_HIDE = 4; + static final int OP_SHOW = 5; + static final int OP_DETACH = 6; + static final int OP_ATTACH = 7; + + static final class Op { + Op next; + Op prev; + int cmd; + Fragment fragment; + int enterAnim; + int exitAnim; + int popEnterAnim; + int popExitAnim; + ArrayList removed; + } + + Op mHead; + Op mTail; + int mNumOp; + int mEnterAnim; + int mExitAnim; + int mPopEnterAnim; + int mPopExitAnim; + int mTransition; + int mTransitionStyle; + boolean mAddToBackStack; + boolean mAllowAddToBackStack = true; + String mName; + boolean mCommitted; + int mIndex; + + int mBreadCrumbTitleRes; + CharSequence mBreadCrumbTitleText; + int mBreadCrumbShortTitleRes; + CharSequence mBreadCrumbShortTitleText; + + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + writer.print(prefix); writer.print("mName="); writer.print(mName); + writer.print(" mIndex="); writer.print(mIndex); + writer.print(" mCommitted="); writer.println(mCommitted); + if (mTransition != FragmentTransaction.TRANSIT_NONE) { + writer.print(prefix); writer.print("mTransition=#"); + writer.print(Integer.toHexString(mTransition)); + writer.print(" mTransitionStyle=#"); + writer.println(Integer.toHexString(mTransitionStyle)); + } + if (mEnterAnim != 0 || mExitAnim !=0) { + writer.print(prefix); writer.print("mEnterAnim=#"); + writer.print(Integer.toHexString(mEnterAnim)); + writer.print(" mExitAnim=#"); + writer.println(Integer.toHexString(mExitAnim)); + } + if (mPopEnterAnim != 0 || mPopExitAnim !=0) { + writer.print(prefix); writer.print("mPopEnterAnim=#"); + writer.print(Integer.toHexString(mPopEnterAnim)); + writer.print(" mPopExitAnim=#"); + writer.println(Integer.toHexString(mPopExitAnim)); + } + if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) { + writer.print(prefix); writer.print("mBreadCrumbTitleRes=#"); + writer.print(Integer.toHexString(mBreadCrumbTitleRes)); + writer.print(" mBreadCrumbTitleText="); + writer.println(mBreadCrumbTitleText); + } + if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) { + writer.print(prefix); writer.print("mBreadCrumbShortTitleRes=#"); + writer.print(Integer.toHexString(mBreadCrumbShortTitleRes)); + writer.print(" mBreadCrumbShortTitleText="); + writer.println(mBreadCrumbShortTitleText); + } + + if (mHead != null) { + writer.print(prefix); writer.println("Operations:"); + String innerPrefix = prefix + " "; + Op op = mHead; + int num = 0; + while (op != null) { + writer.print(prefix); writer.print(" Op #"); writer.print(num); + writer.println(":"); + writer.print(innerPrefix); writer.print("cmd="); writer.print(op.cmd); + writer.print(" fragment="); writer.println(op.fragment); + if (op.enterAnim != 0 || op.exitAnim != 0) { + writer.print(prefix); writer.print("enterAnim=#"); + writer.print(Integer.toHexString(op.enterAnim)); + writer.print(" exitAnim=#"); + writer.println(Integer.toHexString(op.exitAnim)); + } + if (op.popEnterAnim != 0 || op.popExitAnim != 0) { + writer.print(prefix); writer.print("popEnterAnim=#"); + writer.print(Integer.toHexString(op.popEnterAnim)); + writer.print(" popExitAnim=#"); + writer.println(Integer.toHexString(op.popExitAnim)); + } + if (op.removed != null && op.removed.size() > 0) { + for (int i=0; i=0; i--) { + Fragment r = op.removed.get(i); + r.mBackStackNesting += amt; + if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " + + r + " to " + r.mBackStackNesting); + } + } + op = op.next; + } + } + + public int commit() { + return commitInternal(false); + } + + public int commitAllowingStateLoss() { + return commitInternal(true); + } + + int commitInternal(boolean allowStateLoss) { + if (mCommitted) throw new IllegalStateException("commit already called"); + if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Commit: " + this); + mCommitted = true; + if (mAddToBackStack) { + mIndex = mManager.allocBackStackIndex(this); + } else { + mIndex = -1; + } + mManager.enqueueAction(this, allowStateLoss); + return mIndex; + } + + public void run() { + if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this); + + if (mAddToBackStack) { + if (mIndex < 0) { + throw new IllegalStateException("addToBackStack() called after commit()"); + } + } + + bumpBackStackNesting(1); + + Op op = mHead; + while (op != null) { + switch (op.cmd) { + case OP_ADD: { + Fragment f = op.fragment; + f.mNextAnim = op.enterAnim; + mManager.addFragment(f, false); + } break; + case OP_REPLACE: { + Fragment f = op.fragment; + if (mManager.mAdded != null) { + for (int i=0; i= 0) { + mManager.freeBackStackIndex(mIndex); + mIndex = -1; + } + } + + public String getName() { + return mName; + } + + public int getTransition() { + return mTransition; + } + + public int getTransitionStyle() { + return mTransitionStyle; + } + + public boolean isEmpty() { + return mNumOp == 0; + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/app/DialogFragment.java b/actionbarsherlock/library/src/android/support/v4/app/DialogFragment.java new file mode 100644 index 000000000..952659d11 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/DialogFragment.java @@ -0,0 +1,396 @@ +/* + * 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 android.support.v4.app; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +/** + * Static library support version of the framework's {@link android.app.DialogFragment}. + * Used to write apps that run on platforms prior to Android 3.0. When running + * on Android 3.0 or above, this implementation is still used; it does not try + * to switch to the framework's implementation. See the framework SDK + * documentation for a class overview. + */ +public class DialogFragment extends Fragment + implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { + + /** + * Style for {@link #setStyle(int, int)}: a basic, + * normal dialog. + */ + public static final int STYLE_NORMAL = 0; + + /** + * Style for {@link #setStyle(int, int)}: don't include + * a title area. + */ + public static final int STYLE_NO_TITLE = 1; + + /** + * Style for {@link #setStyle(int, int)}: don't draw + * any frame at all; the view hierarchy returned by {@link #onCreateView} + * is entirely responsible for drawing the dialog. + */ + public static final int STYLE_NO_FRAME = 2; + + /** + * Style for {@link #setStyle(int, int)}: like + * {@link #STYLE_NO_FRAME}, but also disables all input to the dialog. + * The user can not touch it, and its window will not receive input focus. + */ + public static final int STYLE_NO_INPUT = 3; + + private static final String SAVED_DIALOG_STATE_TAG = "android:savedDialogState"; + private static final String SAVED_STYLE = "android:style"; + private static final String SAVED_THEME = "android:theme"; + private static final String SAVED_CANCELABLE = "android:cancelable"; + private static final String SAVED_SHOWS_DIALOG = "android:showsDialog"; + private static final String SAVED_BACK_STACK_ID = "android:backStackId"; + + int mStyle = STYLE_NORMAL; + int mTheme = 0; + boolean mCancelable = true; + boolean mShowsDialog = false; + int mBackStackId = -1; + + Dialog mDialog; + boolean mDestroyed; + boolean mRemoved; + + public DialogFragment() { + } + + /** + * Call to customize the basic appearance and behavior of the + * fragment's dialog. This can be used for some common dialog behaviors, + * taking care of selecting flags, theme, and other options for you. The + * same effect can be achieve by manually setting Dialog and Window + * attributes yourself. Calling this after the fragment's Dialog is + * created will have no effect. + * + * @param style Selects a standard style: may be {@link #STYLE_NORMAL}, + * {@link #STYLE_NO_TITLE}, {@link #STYLE_NO_FRAME}, or + * {@link #STYLE_NO_INPUT}. + * @param theme Optional custom theme. If 0, an appropriate theme (based + * on the style) will be selected for you. + */ + public void setStyle(int style, int theme) { + mStyle = style; + if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) { + mTheme = android.R.style.Theme_Panel; + } + if (theme != 0) { + mTheme = theme; + } + } + + /** + * Display the dialog, adding the fragment to the given FragmentManager. This + * is a convenience for explicitly creating a transaction, adding the + * fragment to it with the given tag, and committing it. This does + * not add the transaction to the back stack. When the fragment + * is dismissed, a new transaction will be executed to remove it from + * the activity. + * @param manager The FragmentManager this fragment will be added to. + * @param tag The tag for this fragment, as per + * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}. + */ + public void show(FragmentManager manager, String tag) { + setShowsDialog(true); + FragmentTransaction ft = manager.beginTransaction(); + ft.add(this, tag); + ft.commit(); + } + + /** + * Display the dialog, adding the fragment using an existing transaction + * and then committing the transaction. + * @param transaction An existing transaction in which to add the fragment. + * @param tag The tag for this fragment, as per + * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}. + * @return Returns the identifier of the committed transaction, as per + * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. + */ + public int show(FragmentTransaction transaction, String tag) { + setShowsDialog(true); + transaction.add(this, tag); + mRemoved = false; + mBackStackId = transaction.commit(); + return mBackStackId; + } + + /** + * Dismiss the fragment and its dialog. If the fragment was added to the + * back stack, all back stack state up to and including this entry will + * be popped. Otherwise, a new transaction will be committed to remove + * the fragment. + */ + public void dismiss() { + dismissInternal(false); + } + + void dismissInternal(boolean allowStateLoss) { + if (mDialog != null) { + mDialog.dismiss(); + mDialog = null; + } + mRemoved = true; + if (mBackStackId >= 0) { + getFragmentManager().popBackStack(mBackStackId, + FragmentManager.POP_BACK_STACK_INCLUSIVE); + mBackStackId = -1; + } else { + FragmentTransaction ft = getFragmentManager().beginTransaction(); + ft.remove(this); + if (allowStateLoss) { + ft.commitAllowingStateLoss(); + } else { + ft.commit(); + } + } + } + + public Dialog getDialog() { + return mDialog; + } + + public int getTheme() { + return mTheme; + } + + /** + * Control whether the shown Dialog is cancelable. Use this instead of + * directly calling {@link Dialog#setCancelable(boolean) + * Dialog.setCancelable(boolean)}, because DialogFragment needs to change + * its behavior based on this. + * + * @param cancelable If true, the dialog is cancelable. The default + * is true. + */ + public void setCancelable(boolean cancelable) { + mCancelable = cancelable; + if (mDialog != null) mDialog.setCancelable(cancelable); + } + + /** + * Return the current value of {@link #setCancelable(boolean)}. + */ + public boolean isCancelable() { + return mCancelable; + } + + /** + * Controls whether this fragment should be shown in a dialog. If not + * set, no Dialog will be created in {@link #onActivityCreated(Bundle)}, + * and the fragment's view hierarchy will thus not be added to it. This + * allows you to instead use it as a normal fragment (embedded inside of + * its activity). + * + *

This is normally set for you based on whether the fragment is + * associated with a container view ID passed to + * {@link FragmentTransaction#add(int, Fragment) FragmentTransaction.add(int, Fragment)}. + * If the fragment was added with a container, setShowsDialog will be + * initialized to false; otherwise, it will be true. + * + * @param showsDialog If true, the fragment will be displayed in a Dialog. + * If false, no Dialog will be created and the fragment's view hierarchly + * left undisturbed. + */ + public void setShowsDialog(boolean showsDialog) { + mShowsDialog = showsDialog; + } + + /** + * Return the current value of {@link #setShowsDialog(boolean)}. + */ + public boolean getShowsDialog() { + return mShowsDialog; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //SEE: http://stackoverflow.com/questions/5637894/dialogfragments-with-devices-api-level-11/7560686#7560686 + //mShowsDialog = mContainerId == 0; + + if (savedInstanceState != null) { + mStyle = savedInstanceState.getInt(SAVED_STYLE, STYLE_NORMAL); + mTheme = savedInstanceState.getInt(SAVED_THEME, 0); + mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true); + mShowsDialog = savedInstanceState.getBoolean(SAVED_SHOWS_DIALOG, mShowsDialog); + mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1); + } + + } + + /** @hide */ + @Override + public LayoutInflater getLayoutInflater(Bundle savedInstanceState) { + if (!mShowsDialog) { + return super.getLayoutInflater(savedInstanceState); + } + + mDialog = onCreateDialog(savedInstanceState); + mDestroyed = false; + switch (mStyle) { + case STYLE_NO_INPUT: + mDialog.getWindow().addFlags( + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); + // fall through... + case STYLE_NO_FRAME: + case STYLE_NO_TITLE: + mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + } + return (LayoutInflater)mDialog.getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + } + + /** + * Override to build your own custom Dialog container. This is typically + * used to show an AlertDialog instead of a generic Dialog; when doing so, + * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} does not need + * to be implemented since the AlertDialog takes care of its own content. + * + *

This method will be called after {@link #onCreate(Bundle)} and + * before {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}. The + * default implementation simply instantiates and returns a {@link Dialog} + * class. + * + *

Note: DialogFragment own the {@link Dialog#setOnCancelListener + * Dialog.setOnCancelListener} and {@link Dialog#setOnDismissListener + * Dialog.setOnDismissListener} callbacks. You must not set them yourself. + * To find out about these events, override {@link #onCancel(DialogInterface)} + * and {@link #onDismiss(DialogInterface)}.

+ * + * @param savedInstanceState The last saved instance state of the Fragment, + * or null if this is a freshly created Fragment. + * + * @return Return a new Dialog instance to be displayed by the Fragment. + */ + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new Dialog(getActivity(), getTheme()); + } + + public void onCancel(DialogInterface dialog) { + } + + public void onDismiss(DialogInterface dialog) { + if (!mRemoved) { + // Note: we need to use allowStateLoss, because the dialog + // dispatches this asynchronously so we can receive the call + // after the activity is paused. Worst case, when the user comes + // back to the activity they see the dialog again. + dismissInternal(true); + } + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + if (!mShowsDialog) { + return; + } + + View view = getView(); + if (view != null) { + if (view.getParent() != null) { + throw new IllegalStateException("DialogFragment can not be attached to a container view"); + } + mDialog.setContentView(view); + } + mDialog.setOwnerActivity(getActivity()); + mDialog.setCancelable(mCancelable); + mDialog.setOnCancelListener(this); + mDialog.setOnDismissListener(this); + if (savedInstanceState != null) { + Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG); + if (dialogState != null) { + mDialog.onRestoreInstanceState(dialogState); + } + } + } + + @Override + public void onStart() { + super.onStart(); + if (mDialog != null) { + mRemoved = false; + mDialog.show(); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + if (mDialog != null) { + Bundle dialogState = mDialog.onSaveInstanceState(); + if (dialogState != null) { + outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState); + } + } + if (mStyle != STYLE_NORMAL) { + outState.putInt(SAVED_STYLE, mStyle); + } + if (mTheme != 0) { + outState.putInt(SAVED_THEME, mTheme); + } + if (!mCancelable) { + outState.putBoolean(SAVED_CANCELABLE, mCancelable); + } + if (mShowsDialog) { + outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog); + } + if (mBackStackId != -1) { + outState.putInt(SAVED_BACK_STACK_ID, mBackStackId); + } + } + + @Override + public void onStop() { + super.onStop(); + if (mDialog != null) { + mDialog.hide(); + } + } + + /** + * Remove dialog. + */ + @Override + public void onDestroyView() { + super.onDestroyView(); + mDestroyed = true; + if (mDialog != null) { + // Set removed here because this dismissal is just to hide + // the dialog -- we don't want this to cause the fragment to + // actually be removed. + mRemoved = true; + mDialog.dismiss(); + mDialog = null; + } + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/app/Fragment.java b/actionbarsherlock/library/src/android/support/v4/app/Fragment.java new file mode 100644 index 000000000..4199a0915 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/Fragment.java @@ -0,0 +1,1348 @@ +/* + * 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 android.support.v4.app; + +import android.app.Activity; +import android.content.ComponentCallbacks; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v4.util.DebugUtils; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.View.OnCreateContextMenuListener; +import android.view.animation.Animation; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.HashMap; + +final class FragmentState implements Parcelable { + final String mClassName; + final int mIndex; + final boolean mFromLayout; + final int mFragmentId; + final int mContainerId; + final String mTag; + final boolean mRetainInstance; + final boolean mDetached; + final Bundle mArguments; + + Bundle mSavedFragmentState; + + Fragment mInstance; + + public FragmentState(Fragment frag) { + mClassName = frag.getClass().getName(); + mIndex = frag.mIndex; + mFromLayout = frag.mFromLayout; + mFragmentId = frag.mFragmentId; + mContainerId = frag.mContainerId; + mTag = frag.mTag; + mRetainInstance = frag.mRetainInstance; + mDetached = frag.mDetached; + mArguments = frag.mArguments; + } + + public FragmentState(Parcel in) { + mClassName = in.readString(); + mIndex = in.readInt(); + mFromLayout = in.readInt() != 0; + mFragmentId = in.readInt(); + mContainerId = in.readInt(); + mTag = in.readString(); + mRetainInstance = in.readInt() != 0; + mDetached = in.readInt() != 0; + mArguments = in.readBundle(); + mSavedFragmentState = in.readBundle(); + } + + public Fragment instantiate(SupportActivity activity) { + if (mInstance != null) { + return mInstance; + } + + if (mArguments != null) { + mArguments.setClassLoader(activity.getClassLoader()); + } + + mInstance = Fragment.instantiate(activity.asActivity(), mClassName, mArguments); + + if (mSavedFragmentState != null) { + mSavedFragmentState.setClassLoader(activity.getClassLoader()); + mInstance.mSavedFragmentState = mSavedFragmentState; + } + mInstance.setIndex(mIndex); + mInstance.mFromLayout = mFromLayout; + mInstance.mRestored = true; + mInstance.mFragmentId = mFragmentId; + mInstance.mContainerId = mContainerId; + mInstance.mTag = mTag; + mInstance.mRetainInstance = mRetainInstance; + mInstance.mDetached = mDetached; + mInstance.mFragmentManager = activity.getInternalCallbacks().getFragments(); + + return mInstance; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mClassName); + dest.writeInt(mIndex); + dest.writeInt(mFromLayout ? 1 : 0); + dest.writeInt(mFragmentId); + dest.writeInt(mContainerId); + dest.writeString(mTag); + dest.writeInt(mRetainInstance ? 1 : 0); + dest.writeInt(mDetached ? 1 : 0); + dest.writeBundle(mArguments); + dest.writeBundle(mSavedFragmentState); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public FragmentState createFromParcel(Parcel in) { + return new FragmentState(in); + } + + public FragmentState[] newArray(int size) { + return new FragmentState[size]; + } + }; +} + +/** + * Static library support version of the framework's {@link android.app.Fragment}. + * Used to write apps that run on platforms prior to Android 3.0. When running + * on Android 3.0 or above, this implementation is still used; it does not try + * to switch to the framework's implementation. See the framework SDK + * documentation for a class overview. + */ +public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener { + private static final HashMap> sClassMap = + new HashMap>(); + + static final int INITIALIZING = 0; // Not yet created. + static final int CREATED = 1; // Created. + static final int ACTIVITY_CREATED = 2; // The activity has finished its creation. + static final int STOPPED = 3; // Fully created, not started. + static final int STARTED = 4; // Created and started, not resumed. + static final int RESUMED = 5; // Created started and resumed. + + int mState = INITIALIZING; + + // Non-null if the fragment's view hierarchy is currently animating away, + // meaning we need to wait a bit on completely destroying it. This is the + // view that is animating. + View mAnimatingAway; + + // If mAnimatingAway != null, this is the state we should move to once the + // animation is done. + int mStateAfterAnimating; + + // When instantiated from saved state, this is the saved state. + Bundle mSavedFragmentState; + SparseArray mSavedViewState; + + // Index into active fragment array. + int mIndex = -1; + + // Internal unique name for this fragment; + String mWho; + + // Construction arguments; + Bundle mArguments; + + // Target fragment. + Fragment mTarget; + + // For use when retaining a fragment: this is the index of the last mTarget. + int mTargetIndex = -1; + + // Target request code. + int mTargetRequestCode; + + // True if the fragment is in the list of added fragments. + boolean mAdded; + + // If set this fragment is being removed from its activity. + boolean mRemoving; + + // True if the fragment is in the resumed state. + boolean mResumed; + + // Set to true if this fragment was instantiated from a layout file. + boolean mFromLayout; + + // Set to true when the view has actually been inflated in its layout. + boolean mInLayout; + + // True if this fragment has been restored from previously saved state. + boolean mRestored; + + // Number of active back stack entries this fragment is in. + int mBackStackNesting; + + // The fragment manager we are associated with. Set as soon as the + // fragment is used in a transaction; cleared after it has been removed + // from all transactions. + FragmentManager mFragmentManager; + + // Activity this fragment is attached to. + SupportActivity mActivity; + + // The optional identifier for this fragment -- either the container ID if it + // was dynamically added to the view hierarchy, or the ID supplied in + // layout. + int mFragmentId; + + // When a fragment is being dynamically added to the view hierarchy, this + // is the identifier of the parent container it is being added to. + int mContainerId; + + // The optional named tag for this fragment -- usually used to find + // fragments that are not part of the layout. + String mTag; + + // Set to true when the app has requested that this fragment be hidden + // from the user. + boolean mHidden; + + // Set to true when the app has requested that this fragment be deactivated. + boolean mDetached; + + // If set this fragment would like its instance retained across + // configuration changes. + boolean mRetainInstance; + + // If set this fragment is being retained across the current config change. + boolean mRetaining; + + // If set this fragment has menu items to contribute. + boolean mHasMenu; + + // Set to true to allow the fragment's menu to be shown. + boolean mMenuVisible = true; + + // Used to verify that subclasses call through to super class. + boolean mCalled; + + // If app has requested a specific animation, this is the one to use. + int mNextAnim; + + // The parent container of the fragment after dynamically added to UI. + ViewGroup mContainer; + + // The View generated for this fragment. + View mView; + + // The real inner view that will save/restore state. + View mInnerView; + + LoaderManagerImpl mLoaderManager; + boolean mLoadersStarted; + boolean mCheckedForLoaderManager; + + /** + * State information that has been retrieved from a fragment instance + * through {@link FragmentManager#saveFragmentInstanceState(Fragment) + * FragmentManager.saveFragmentInstanceState}. + */ + public static class SavedState implements Parcelable { + final Bundle mState; + + SavedState(Bundle state) { + mState = state; + } + + SavedState(Parcel in, ClassLoader loader) { + mState = in.readBundle(); + if (loader != null && mState != null) { + mState.setClassLoader(loader); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBundle(mState); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in, null); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + /** + * Thrown by {@link Fragment#instantiate(Context, String, Bundle)} when + * there is an instantiation failure. + */ + static public class InstantiationException extends RuntimeException { + private static final long serialVersionUID = 8423238441973733190L; + + public InstantiationException(String msg, Exception cause) { + super(msg, cause); + } + } + + /** + * Default constructor. Every fragment must have an + * empty constructor, so it can be instantiated when restoring its + * activity's state. It is strongly recommended that subclasses do not + * have other constructors with parameters, since these constructors + * will not be called when the fragment is re-instantiated; instead, + * arguments can be supplied by the caller with {@link #setArguments} + * and later retrieved by the Fragment with {@link #getArguments}. + * + *

Applications should generally not implement a constructor. The + * first place application code an run where the fragment is ready to + * be used is in {@link #onAttach(Activity)}, the point where the fragment + * is actually associated with its activity. Some applications may also + * want to implement {@link #onInflate} to retrieve attributes from a + * layout resource, though should take care here because this happens for + * the fragment is attached to its activity. + */ + public Fragment() { + } + + /** + * Like {@link #instantiate(Context, String, Bundle)} but with a null + * argument Bundle. + */ + public static Fragment instantiate(Context context, String fname) { + return instantiate(context, fname, null); + } + + /** + * Create a new instance of a Fragment with the given class name. This is + * the same as calling its empty constructor. + * + * @param context The calling context being used to instantiate the fragment. + * This is currently just used to get its ClassLoader. + * @param fname The class name of the fragment to instantiate. + * @param args Bundle of arguments to supply to the fragment, which it + * can retrieve with {@link #getArguments()}. May be null. + * @return Returns a new fragment instance. + * @throws InstantiationException If there is a failure in instantiating + * the given fragment class. This is a runtime exception; it is not + * normally expected to happen. + */ + public static Fragment instantiate(Context context, String fname, Bundle args) { + try { + Class clazz = sClassMap.get(fname); + if (clazz == null) { + // Class not found in the cache, see if it's real, and try to add it + clazz = context.getClassLoader().loadClass(fname); + sClassMap.put(fname, clazz); + } + Fragment f = (Fragment)clazz.newInstance(); + if (args != null) { + args.setClassLoader(f.getClass().getClassLoader()); + f.mArguments = args; + } + return f; + } catch (ClassNotFoundException e) { + throw new InstantiationException("Unable to instantiate fragment " + fname + + ": make sure class name exists, is public, and has an" + + " empty constructor that is public", e); + } catch (java.lang.InstantiationException e) { + throw new InstantiationException("Unable to instantiate fragment " + fname + + ": make sure class name exists, is public, and has an" + + " empty constructor that is public", e); + } catch (IllegalAccessException e) { + throw new InstantiationException("Unable to instantiate fragment " + fname + + ": make sure class name exists, is public, and has an" + + " empty constructor that is public", e); + } + } + + final void restoreViewState() { + if (mSavedViewState != null) { + mInnerView.restoreHierarchyState(mSavedViewState); + mSavedViewState = null; + } + } + + final void setIndex(int index) { + mIndex = index; + mWho = "android:fragment:" + mIndex; + } + + final boolean isInBackStack() { + return mBackStackNesting > 0; + } + + /** + * Subclasses can not override equals(). + */ + @Override final public boolean equals(Object o) { + return super.equals(o); + } + + /** + * Subclasses can not override hashCode(). + */ + @Override final public int hashCode() { + return super.hashCode(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + DebugUtils.buildShortClassTag(this, sb); + if (mIndex >= 0) { + sb.append(" #"); + sb.append(mIndex); + } + if (mFragmentId != 0) { + sb.append(" id=0x"); + sb.append(Integer.toHexString(mFragmentId)); + } + if (mTag != null) { + sb.append(" "); + sb.append(mTag); + } + sb.append('}'); + return sb.toString(); + } + + /** + * Return the identifier this fragment is known by. This is either + * the android:id value supplied in a layout or the container view ID + * supplied when adding the fragment. + */ + final public int getId() { + return mFragmentId; + } + + /** + * Get the tag name of the fragment, if specified. + */ + final public String getTag() { + return mTag; + } + + /** + * Supply the construction arguments for this fragment. This can only + * be called before the fragment has been attached to its activity; that + * is, you should call it immediately after constructing the fragment. The + * arguments supplied here will be retained across fragment destroy and + * creation. + */ + public void setArguments(Bundle args) { + if (mIndex >= 0) { + throw new IllegalStateException("Fragment already active"); + } + mArguments = args; + } + + /** + * Return the arguments supplied when the fragment was instantiated, + * if any. + */ + final public Bundle getArguments() { + return mArguments; + } + + /** + * Set the initial saved state that this Fragment should restore itself + * from when first being constructed, as returned by + * {@link FragmentManager#saveFragmentInstanceState(Fragment) + * FragmentManager.saveFragmentInstanceState}. + * + * @param state The state the fragment should be restored from. + */ + public void setInitialSavedState(SavedState state) { + if (mIndex >= 0) { + throw new IllegalStateException("Fragment already active"); + } + mSavedFragmentState = state != null && state.mState != null + ? state.mState : null; + } + + /** + * Optional target for this fragment. This may be used, for example, + * if this fragment is being started by another, and when done wants to + * give a result back to the first. The target set here is retained + * across instances via {@link FragmentManager#putFragment + * FragmentManager.putFragment()}. + * + * @param fragment The fragment that is the target of this one. + * @param requestCode Optional request code, for convenience if you + * are going to call back with {@link #onActivityResult(int, int, Intent)}. + */ + public void setTargetFragment(Fragment fragment, int requestCode) { + mTarget = fragment; + mTargetRequestCode = requestCode; + } + + /** + * Return the target fragment set by {@link #setTargetFragment}. + */ + final public Fragment getTargetFragment() { + return mTarget; + } + + /** + * Return the target request code set by {@link #setTargetFragment}. + */ + final public int getTargetRequestCode() { + return mTargetRequestCode; + } + + /** + * Return the Activity this fragment is currently associated with. + * + * @see #getSupportActivity() + */ + final public Activity getActivity() { + return (mActivity != null) ? mActivity.asActivity() : null; + } + + /** + * Return the SupportActivity interface for the activity in which this + * fragment is currently associated. + * + * @see #getActivity() + */ + final public SupportActivity getSupportActivity() { + return mActivity; + } + + /** + * Return getActivity().getResources(). + */ + final public Resources getResources() { + if (mActivity == null) { + throw new IllegalStateException("Fragment " + this + " not attached to Activity"); + } + return mActivity.getResources(); + } + + /** + * Return a localized, styled CharSequence from the application's package's + * default string table. + * + * @param resId Resource id for the CharSequence text + */ + public final CharSequence getText(int resId) { + return getResources().getText(resId); + } + + /** + * Return a localized string from the application's package's + * default string table. + * + * @param resId Resource id for the string + */ + public final String getString(int resId) { + return getResources().getString(resId); + } + + /** + * Return a localized formatted string from the application's package's + * default string table, substituting the format arguments as defined in + * {@link java.util.Formatter} and {@link java.lang.String#format}. + * + * @param resId Resource id for the format string + * @param formatArgs The format arguments that will be used for substitution. + */ + + public final String getString(int resId, Object... formatArgs) { + return getResources().getString(resId, formatArgs); + } + + /** + * Return the FragmentManager for interacting with fragments associated + * with this fragment's activity. Note that this will be non-null slightly + * before {@link #getActivity()}, during the time from when the fragment is + * placed in a {@link FragmentTransaction} until it is committed and + * attached to its activity. + */ + final public FragmentManager getFragmentManager() { + return mFragmentManager; + } + + /** + * A clone of {@link #getFragmentManager()} to maintain naming conventions + * throughout the compatibility library. + */ + final public FragmentManager getSupportFragmentManager() { + return mFragmentManager; + } + + /** + * Return true if the fragment is currently added to its activity. + */ + final public boolean isAdded() { + return mActivity != null && mAdded; + } + + /** + * Return true if the fragment has been explicitly detached from the UI. + * That is, {@link FragmentTransaction#detach(Fragment) + * FragmentTransaction.detach(Fragment)} has been used on it. + */ + final public boolean isDetached() { + return mDetached; + } + + /** + * Return true if this fragment is currently being removed from its + * activity. This is not whether its activity is finishing, but + * rather whether it is in the process of being removed from its activity. + */ + final public boolean isRemoving() { + return mRemoving; + } + + /** + * Return true if the layout is included as part of an activity view + * hierarchy via the <fragment> tag. This will always be true when + * fragments are created through the <fragment> tag, except + * in the case where an old fragment is restored from a previous state and + * it does not appear in the layout of the current state. + */ + final public boolean isInLayout() { + return mInLayout; + } + + /** + * Return true if the fragment is in the resumed state. This is true + * for the duration of {@link #onResume()} and {@link #onPause()} as well. + */ + final public boolean isResumed() { + return mResumed; + } + + /** + * Return true if the fragment is currently visible to the user. This means + * it: (1) has been added, (2) has its view attached to the window, and + * (3) is not hidden. + */ + final public boolean isVisible() { + return isAdded() && !isHidden() && mView != null + && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE; + } + + /** + * Return true if the fragment has been hidden. By default fragments + * are shown. You can find out about changes to this state with + * {@link #onHiddenChanged}. Note that the hidden state is orthogonal + * to other states -- that is, to be visible to the user, a fragment + * must be both started and not hidden. + */ + final public boolean isHidden() { + return mHidden; + } + + /** + * Called when the hidden state (as returned by {@link #isHidden()} of + * the fragment has changed. Fragments start out not hidden; this will + * be called whenever the fragment changes state from that. + * @param hidden True if the fragment is now hidden, false if it is not + * visible. + */ + public void onHiddenChanged(boolean hidden) { + } + + /** + * Control whether a fragment instance is retained across Activity + * re-creation (such as from a configuration change). This can only + * be used with fragments not in the back stack. If set, the fragment + * lifecycle will be slightly different when an activity is recreated: + *

    + *
  • {@link #onDestroy()} will not be called (but {@link #onDetach()} still + * will be, because the fragment is being detached from its current activity). + *
  • {@link #onCreate(Bundle)} will not be called since the fragment + * is not being re-created. + *
  • {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} will + * still be called. + *
+ */ + public void setRetainInstance(boolean retain) { + mRetainInstance = retain; + } + + final public boolean getRetainInstance() { + return mRetainInstance; + } + + /** + * Report that this fragment would like to participate in populating + * the options menu by receiving a call to {@link #onCreateOptionsMenu} + * and related methods. + * + * @param hasMenu If true, the fragment has menu items to contribute. + */ + public void setHasOptionsMenu(boolean hasMenu) { + if (mHasMenu != hasMenu) { + mHasMenu = hasMenu; + if (isAdded() && !isHidden()) { + mActivity.invalidateOptionsMenu(); + } + } + } + + /** + * Set a hint for whether this fragment's menu should be visible. This + * is useful if you know that a fragment has been placed in your view + * hierarchy so that the user can not currently seen it, so any menu items + * it has should also not be shown. + * + * @param menuVisible The default is true, meaning the fragment's menu will + * be shown as usual. If false, the user will not see the menu. + */ + public void setMenuVisibility(boolean menuVisible) { + if (mMenuVisible != menuVisible) { + mMenuVisible = menuVisible; + if (mHasMenu && isAdded() && !isHidden()) { + mActivity.invalidateOptionsMenu(); + } + } + } + + /** + * Return the LoaderManager for this fragment, creating it if needed. + */ + public LoaderManager getLoaderManager() { + if (mLoaderManager != null) { + return mLoaderManager; + } + if (mActivity == null) { + throw new IllegalStateException("Fragment " + this + " not attached to Activity"); + } + mCheckedForLoaderManager = true; + mLoaderManager = mActivity.getInternalCallbacks().getLoaderManager(mIndex, mLoadersStarted, true); + return mLoaderManager; + } + + /** + * Call {@link Activity#startActivity(Intent)} on the fragment's + * containing Activity. + */ + public void startActivity(Intent intent) { + if (mActivity == null) { + throw new IllegalStateException("Fragment " + this + " not attached to Activity"); + } + mActivity.startActivityFromFragment(this, intent, -1); + } + + /** + * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's + * containing Activity. + */ + public void startActivityForResult(Intent intent, int requestCode) { + if (mActivity == null) { + throw new IllegalStateException("Fragment " + this + " not attached to Activity"); + } + mActivity.startActivityFromFragment(this, intent, requestCode); + } + + /** + * Receive the result from a previous call to + * {@link #startActivityForResult(Intent, int)}. This follows the + * related Activity API as described there in + * {@link Activity#onActivityResult(int, int, Intent)}. + * + * @param requestCode The integer request code originally supplied to + * startActivityForResult(), allowing you to identify who this + * result came from. + * @param resultCode The integer result code returned by the child activity + * through its setResult(). + * @param data An Intent, which can return result data to the caller + * (various data can be attached to Intent "extras"). + */ + public void onActivityResult(int requestCode, int resultCode, Intent data) { + } + + /** + * @hide Hack so that DialogFragment can make its Dialog before creating + * its views, and the view construction can use the dialog's context for + * inflation. Maybe this should become a public API. Note sure. + */ + public LayoutInflater getLayoutInflater(Bundle savedInstanceState) { + return mActivity.getLayoutInflater(); + } + + /** + * Called when a fragment is being created as part of a view layout + * inflation, typically from setting the content view of an activity. This + * may be called immediately after the fragment is created from a + * tag in a layout file. Note this is before the fragment's + * {@link #onAttach(Activity)} has been called; all you should do here is + * parse the attributes and save them away. + * + *

This is called every time the fragment is inflated, even if it is + * being inflated into a new instance with saved state. It typically makes + * sense to re-parse the parameters each time, to allow them to change with + * different configurations.

+ * + *

Here is a typical implementation of a fragment that can take parameters + * both through attributes supplied here as well from {@link #getArguments()}:

+ * + * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentArguments.java + * fragment} + * + *

Note that parsing the XML attributes uses a "styleable" resource. The + * declaration for the styleable used here is:

+ * + * {@sample development/samples/ApiDemos/res/values/attrs.xml fragment_arguments} + * + *

The fragment can then be declared within its activity's content layout + * through a tag like this:

+ * + * {@sample development/samples/ApiDemos/res/layout/fragment_arguments.xml from_attributes} + * + *

This fragment can also be created dynamically from arguments given + * at runtime in the arguments Bundle; here is an example of doing so at + * creation of the containing activity:

+ * + * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentArguments.java + * create} + * + * @param activity The Activity that is inflating this fragment. + * @param attrs The attributes at the tag where the fragment is + * being created. + * @param savedInstanceState If the fragment is being re-created from + * a previous saved state, this is the state. + */ + public void onInflate(SupportActivity activity, AttributeSet attrs, Bundle savedInstanceState) { + mCalled = true; + } + + /** + * Called when a fragment is first attached to its activity. + * {@link #onCreate(Bundle)} will be called after this. + */ + public void onAttach(SupportActivity activity) { + mCalled = true; + } + + /** + * Called when a fragment loads an animation. + */ + public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { + return null; + } + + /** + * Called to do initial creation of a fragment. This is called after + * {@link #onAttach(Activity)} and before + * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}. + * + *

Note that this can be called while the fragment's activity is + * still in the process of being created. As such, you can not rely + * on things like the activity's content view hierarchy being initialized + * at this point. If you want to do work once the activity itself is + * created, see {@link #onActivityCreated(Bundle)}. + * + * @param savedInstanceState If the fragment is being re-created from + * a previous saved state, this is the state. + */ + public void onCreate(Bundle savedInstanceState) { + mCalled = true; + } + + /** + * Called to have the fragment instantiate its user interface view. + * This is optional, and non-graphical fragments can return null (which + * is the default implementation). This will be called between + * {@link #onCreate(Bundle)} and {@link #onActivityCreated(Bundle)}. + * + *

If you return a View from here, you will later be called in + * {@link #onDestroyView} when the view is being released. + * + * @param inflater The LayoutInflater object that can be used to inflate + * any views in the fragment, + * @param container If non-null, this is the parent view that the fragment's + * UI should be attached to. The fragment should not add the view itself, + * but this can be used to generate the LayoutParams of the view. + * @param savedInstanceState If non-null, this fragment is being re-constructed + * from a previous saved state as given here. + * + * @return Return the View for the fragment's UI, or null. + */ + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return null; + } + + /** + * Called immediately after {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} + * has returned, but before any saved state has been restored in to the view. + * This gives subclasses a chance to initialize themselves once + * they know their view hierarchy has been completely created. The fragment's + * view hierarchy is not however attached to its parent at this point. + * @param view The View returned by {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}. + * @param savedInstanceState If non-null, this fragment is being re-constructed + * from a previous saved state as given here. + */ + public void onViewCreated(View view, Bundle savedInstanceState) { + } + + /** + * Get the root view for the fragment's layout (the one returned by {@link #onCreateView}), + * if provided. + * + * @return The fragment's root view, or null if it has no layout. + */ + public View getView() { + return mView; + } + + /** + * Called when the fragment's activity has been created and this + * fragment's view hierarchy instantiated. It can be used to do final + * initialization once these pieces are in place, such as retrieving + * views or restoring state. It is also useful for fragments that use + * {@link #setRetainInstance(boolean)} to retain their instance, + * as this callback tells the fragment when it is fully associated with + * the new activity instance. This is called after {@link #onCreateView} + * and before {@link #onStart()}. + * + * @param savedInstanceState If the fragment is being re-created from + * a previous saved state, this is the state. + */ + public void onActivityCreated(Bundle savedInstanceState) { + mCalled = true; + } + + /** + * Called when the Fragment is visible to the user. This is generally + * tied to {@link Activity#onStart() Activity.onStart} of the containing + * Activity's lifecycle. + */ + public void onStart() { + mCalled = true; + + if (!mLoadersStarted) { + mLoadersStarted = true; + if (!mCheckedForLoaderManager) { + mCheckedForLoaderManager = true; + mLoaderManager = mActivity.getInternalCallbacks().getLoaderManager(mIndex, mLoadersStarted, false); + } + if (mLoaderManager != null) { + mLoaderManager.doStart(); + } + } + } + + /** + * Called when the fragment is visible to the user and actively running. + * This is generally + * tied to {@link Activity#onResume() Activity.onResume} of the containing + * Activity's lifecycle. + */ + public void onResume() { + mCalled = true; + } + + /** + * Called to ask the fragment to save its current dynamic state, so it + * can later be reconstructed in a new instance of its process is + * restarted. If a new instance of the fragment later needs to be + * created, the data you place in the Bundle here will be available + * in the Bundle given to {@link #onCreate(Bundle)}, + * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}, and + * {@link #onActivityCreated(Bundle)}. + * + *

This corresponds to {@link Activity#onSaveInstanceState(Bundle) + * Activity.onSaveInstanceState(Bundle)} and most of the discussion there + * applies here as well. Note however: this method may be called + * at any time before {@link #onDestroy()}. There are many situations + * where a fragment may be mostly torn down (such as when placed on the + * back stack with no UI showing), but its state will not be saved until + * its owning activity actually needs to save its state. + * + * @param outState Bundle in which to place your saved state. + */ + public void onSaveInstanceState(Bundle outState) { + } + + public void onConfigurationChanged(Configuration newConfig) { + mCalled = true; + } + + /** + * Called when the Fragment is no longer resumed. This is generally + * tied to {@link Activity#onPause() Activity.onPause} of the containing + * Activity's lifecycle. + */ + public void onPause() { + mCalled = true; + } + + /** + * Called when the Fragment is no longer started. This is generally + * tied to {@link Activity#onStop() Activity.onStop} of the containing + * Activity's lifecycle. + */ + public void onStop() { + mCalled = true; + } + + public void onLowMemory() { + mCalled = true; + } + + /** + * Called when the view previously created by {@link #onCreateView} has + * been detached from the fragment. The next time the fragment needs + * to be displayed, a new view will be created. This is called + * after {@link #onStop()} and before {@link #onDestroy()}. It is called + * regardless of whether {@link #onCreateView} returned a + * non-null view. Internally it is called after the view's state has + * been saved but before it has been removed from its parent. + */ + public void onDestroyView() { + mCalled = true; + } + + /** + * Called when the fragment is no longer in use. This is called + * after {@link #onStop()} and before {@link #onDetach()}. + */ + public void onDestroy() { + mCalled = true; + //Log.v("foo", "onDestroy: mCheckedForLoaderManager=" + mCheckedForLoaderManager + // + " mLoaderManager=" + mLoaderManager); + if (!mCheckedForLoaderManager) { + mCheckedForLoaderManager = true; + mLoaderManager = mActivity.getInternalCallbacks().getLoaderManager(mIndex, mLoadersStarted, false); + } + if (mLoaderManager != null) { + mLoaderManager.doDestroy(); + } + } + + /** + * Called by the fragment manager once this fragment has been removed, + * so that we don't have any left-over state if the application decides + * to re-use the instance. This only clears state that the framework + * internally manages, not things the application sets. + */ + void initState() { + mIndex = -1; + mWho = null; + mAdded = false; + mRemoving = false; + mResumed = false; + mFromLayout = false; + mInLayout = false; + mRestored = false; + mBackStackNesting = 0; + mFragmentManager = null; + mActivity = null; + mFragmentId = 0; + mContainerId = 0; + mTag = null; + mHidden = false; + mDetached = false; + mRetaining = false; + mLoaderManager = null; + mLoadersStarted = false; + mCheckedForLoaderManager = false; + } + + /** + * Called when the fragment is no longer attached to its activity. This + * is called after {@link #onDestroy()}. + */ + public void onDetach() { + mCalled = true; + } + + /** + * Initialize the contents of the Activity's standard options menu. You + * should place your menu items in to menu. For this method + * to be called, you must have first called {@link #setHasOptionsMenu}. See + * {@link Activity#onCreateOptionsMenu(Menu) Activity.onCreateOptionsMenu} + * for more information. + * + * @param menu The options menu in which you place your items. + * + * @see #setHasOptionsMenu + * @see #onPrepareOptionsMenu + * @see #onOptionsItemSelected + */ + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + } + + /** + * Prepare the Screen's standard options menu to be displayed. This is + * called right before the menu is shown, every time it is shown. You can + * use this method to efficiently enable/disable items or otherwise + * dynamically modify the contents. See + * {@link Activity#onPrepareOptionsMenu(Menu) Activity.onPrepareOptionsMenu} + * for more information. + * + * @param menu The options menu as last shown or first initialized by + * onCreateOptionsMenu(). + * + * @see #setHasOptionsMenu + * @see #onCreateOptionsMenu + */ + public void onPrepareOptionsMenu(Menu menu) { + } + + /** + * Called when this fragment's option menu items are no longer being + * included in the overall options menu. Receiving this call means that + * the menu needed to be rebuilt, but this fragment's items were not + * included in the newly built menu (its {@link #onCreateOptionsMenu(Menu, MenuInflater)} + * was not called). + */ + public void onDestroyOptionsMenu() { + } + + /** + * This hook is called whenever an item in your options menu is selected. + * The default implementation simply returns false to have the normal + * processing happen (calling the item's Runnable or sending a message to + * its Handler as appropriate). You can use this method for any items + * for which you would like to do processing without those other + * facilities. + * + *

Derived classes should call through to the base class for it to + * perform the default menu handling. + * + * @param item The menu item that was selected. + * + * @return boolean Return false to allow normal menu processing to + * proceed, true to consume it here. + * + * @see #onCreateOptionsMenu + */ + public boolean onOptionsItemSelected(MenuItem item) { + return false; + } + + /** + * This hook is called whenever the options menu is being closed (either by the user canceling + * the menu with the back/menu button, or when an item is selected). + * + * @param menu The options menu as last shown or first initialized by + * onCreateOptionsMenu(). + */ + public void onOptionsMenuClosed(Menu menu) { + } + + /** + * Called when a context menu for the {@code view} is about to be shown. + * Unlike {@link #onCreateOptionsMenu}, this will be called every + * time the context menu is about to be shown and should be populated for + * the view (or item inside the view for {@link AdapterView} subclasses, + * this can be found in the {@code menuInfo})). + *

+ * Use {@link #onContextItemSelected(android.view.MenuItem)} to know when an + * item has been selected. + *

+ * The default implementation calls up to + * {@link Activity#onCreateContextMenu Activity.onCreateContextMenu}, though + * you can not call this implementation if you don't want that behavior. + *

+ * It is not safe to hold onto the context menu after this method returns. + * {@inheritDoc} + */ + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + getActivity().onCreateContextMenu(menu, v, menuInfo); + } + + /** + * Registers a context menu to be shown for the given view (multiple views + * can show the context menu). This method will set the + * {@link OnCreateContextMenuListener} on the view to this fragment, so + * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} will be + * called when it is time to show the context menu. + * + * @see #unregisterForContextMenu(View) + * @param view The view that should show a context menu. + */ + public void registerForContextMenu(View view) { + view.setOnCreateContextMenuListener(this); + } + + /** + * Prevents a context menu to be shown for the given view. This method will + * remove the {@link OnCreateContextMenuListener} on the view. + * + * @see #registerForContextMenu(View) + * @param view The view that should stop showing a context menu. + */ + public void unregisterForContextMenu(View view) { + view.setOnCreateContextMenuListener(null); + } + + /** + * This hook is called whenever an item in a context menu is selected. The + * default implementation simply returns false to have the normal processing + * happen (calling the item's Runnable or sending a message to its Handler + * as appropriate). You can use this method for any items for which you + * would like to do processing without those other facilities. + *

+ * Use {@link MenuItem#getMenuInfo()} to get extra information set by the + * View that added this menu item. + *

+ * Derived classes should call through to the base class for it to perform + * the default menu handling. + * + * @param item The context menu item that was selected. + * @return boolean Return false to allow normal context menu processing to + * proceed, true to consume it here. + */ + public boolean onContextItemSelected(MenuItem item) { + return false; + } + + /** + * Print the Fragments's state into the given stream. + * + * @param prefix Text to print at the front of each line. + * @param fd The raw file descriptor that the dump is being sent to. + * @param writer The PrintWriter to which you should dump your state. This will be + * closed for you after you return. + * @param args additional arguments to the dump request. + */ + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + writer.print(prefix); writer.print("mFragmentId=#"); + writer.print(Integer.toHexString(mFragmentId)); + writer.print(" mContainerId#="); + writer.print(Integer.toHexString(mContainerId)); + writer.print(" mTag="); writer.println(mTag); + writer.print(prefix); writer.print("mState="); writer.print(mState); + writer.print(" mIndex="); writer.print(mIndex); + writer.print(" mWho="); writer.print(mWho); + writer.print(" mBackStackNesting="); writer.println(mBackStackNesting); + writer.print(prefix); writer.print("mAdded="); writer.print(mAdded); + writer.print(" mRemoving="); writer.print(mRemoving); + writer.print(" mResumed="); writer.print(mResumed); + writer.print(" mFromLayout="); writer.print(mFromLayout); + writer.print(" mInLayout="); writer.println(mInLayout); + writer.print(prefix); writer.print("mHidden="); writer.print(mHidden); + writer.print(" mDetached="); writer.print(mDetached); + writer.print(" mMenuVisible="); writer.print(mMenuVisible); + writer.print(" mHasMenu="); writer.println(mHasMenu); + writer.print(prefix); writer.print("mRetainInstance="); writer.print(mRetainInstance); + writer.print(" mRetaining="); writer.println(mRetaining); + if (mFragmentManager != null) { + writer.print(prefix); writer.print("mFragmentManager="); + writer.println(mFragmentManager); + } + if (mActivity != null) { + writer.print(prefix); writer.print("mActivity="); + writer.println(mActivity); + } + if (mArguments != null) { + writer.print(prefix); writer.print("mArguments="); writer.println(mArguments); + } + if (mSavedFragmentState != null) { + writer.print(prefix); writer.print("mSavedFragmentState="); + writer.println(mSavedFragmentState); + } + if (mSavedViewState != null) { + writer.print(prefix); writer.print("mSavedViewState="); + writer.println(mSavedViewState); + } + if (mTarget != null) { + writer.print(prefix); writer.print("mTarget="); writer.print(mTarget); + writer.print(" mTargetRequestCode="); + writer.println(mTargetRequestCode); + } + if (mNextAnim != 0) { + writer.print(prefix); writer.print("mNextAnim="); writer.println(mNextAnim); + } + if (mContainer != null) { + writer.print(prefix); writer.print("mContainer="); writer.println(mContainer); + } + if (mView != null) { + writer.print(prefix); writer.print("mView="); writer.println(mView); + } + if (mInnerView != null) { + writer.print(prefix); writer.print("mInnerView="); writer.println(mView); + } + if (mAnimatingAway != null) { + writer.print(prefix); writer.print("mAnimatingAway="); writer.println(mAnimatingAway); + writer.print(prefix); writer.print("mStateAfterAnimating="); + writer.println(mStateAfterAnimating); + } + if (mLoaderManager != null) { + writer.print(prefix); writer.println("Loader Manager:"); + mLoaderManager.dump(prefix + " ", fd, writer, args); + } + } + + void performStart() { + onStart(); + if (mLoaderManager != null) { + mLoaderManager.doReportStart(); + } + } + + void performStop() { + onStop(); + } + + void performReallyStop() { + if (mLoadersStarted) { + mLoadersStarted = false; + if (!mCheckedForLoaderManager) { + mCheckedForLoaderManager = true; + mLoaderManager = mActivity.getInternalCallbacks().getLoaderManager(mIndex, mLoadersStarted, false); + } + if (mLoaderManager != null) { + if (!mActivity.getInternalCallbacks().getRetaining()) { + mLoaderManager.doStop(); + } else { + mLoaderManager.doRetain(); + } + } + } + } + + void performDestroyView() { + onDestroyView(); + if (mLoaderManager != null) { + mLoaderManager.doReportNextStart(); + } + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/app/FragmentActivity.java b/actionbarsherlock/library/src/android/support/v4/app/FragmentActivity.java new file mode 100644 index 000000000..816d74cf1 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/FragmentActivity.java @@ -0,0 +1,1237 @@ +/* + * Copyright (C) 2011 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 android.support.v4.app; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import com.actionbarsherlock.R; +import com.actionbarsherlock.internal.app.ActionBarWrapper; +import com.actionbarsherlock.internal.app.ActionBarImpl; +import com.actionbarsherlock.internal.view.menu.MenuBuilder; +import com.actionbarsherlock.internal.view.menu.MenuInflaterWrapper; +import com.actionbarsherlock.internal.view.menu.MenuItemImpl; +import com.actionbarsherlock.internal.view.menu.MenuItemWrapper; +import com.actionbarsherlock.internal.view.menu.MenuWrapper; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.content.res.Resources.Theme; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Parcelable; +import android.support.v4.view.ActionMode; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuInflater; +import android.support.v4.view.MenuItem; +import android.support.v4.view.Window; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.widget.FrameLayout; + +/** + * Base class for activities that want to use the support-based ActionBar, + * Fragment, and Loader APIs. + * + *

Known limitations:

+ *
    + *
  • When using the <fragment> tag, this implementation can not + * use the parent view's ID as the new fragment's ID. You must explicitly + * specify an ID (or tag) in the <fragment>.

    + *
  • Prior to Honeycomb (3.0), an activity's state was saved before pausing. + * Fragments are a significant amount of new state, and dynamic enough that one + * often wants them to change between pausing and stopping. These classes + * throw an exception if you try to change the fragment state after it has been + * saved, to avoid accidental loss of UI state. However this is too restrictive + * prior to Honeycomb, where the state is saved before pausing. To address this, + * when running on platforms prior to Honeycomb an exception will not be thrown + * if you change fragments between the state save and the activity being stopped. + * This means that is some cases if the activity is restored from its last saved + * state, this may be a snapshot slightly before what the user last saw.

    + *
+ */ +public class FragmentActivity extends Activity implements SupportActivity { + private static final String TAG = "FragmentActivity"; + private static final boolean DEBUG = false; + + private static final String FRAGMENTS_TAG = "android:support:fragments"; + + static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; + + static final int MSG_REALLY_STOPPED = 1; + static final int MSG_RESUME_PENDING = 2; + + private static final int WINDOW_FLAG_ACTION_BAR = 1 << Window.FEATURE_ACTION_BAR; + private static final int WINDOW_FLAG_ACTION_BAR_ITEM_TEXT = 1 << Window.FEATURE_ACTION_BAR_ITEM_TEXT; + private static final int WINDOW_FLAG_ACTION_BAR_OVERLAY = 1 << Window.FEATURE_ACTION_BAR_OVERLAY; + private static final int WINDOW_FLAG_ACTION_MODE_OVERLAY = 1 << Window.FEATURE_ACTION_MODE_OVERLAY; + private static final int WINDOW_FLAG_INDETERMINANTE_PROGRESS = 1 << Window.FEATURE_INDETERMINATE_PROGRESS; + + final SupportActivity.InternalCallbacks mInternalCallbacks = new SupportActivity.InternalCallbacks() { + @Override + void invalidateSupportFragmentIndex(int index) { + FragmentActivity.this.invalidateSupportFragmentIndex(index); + } + + @Override + LoaderManagerImpl getLoaderManager(int index, boolean started, boolean create) { + return FragmentActivity.this.getLoaderManager(index, started, create); + } + + @Override + Handler getHandler() { + return mHandler; + } + + @Override + FragmentManagerImpl getFragments() { + return mFragments; + } + + @Override + void ensureSupportActionBarAttached() { + FragmentActivity.this.ensureSupportActionBarAttached(); + } + + @Override + boolean getRetaining() { + return mRetaining; + } + }; + + final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_REALLY_STOPPED: + if (mStopped) { + doReallyStop(false); + } + break; + case MSG_RESUME_PENDING: + mFragments.dispatchResume(); + mFragments.execPendingActions(); + break; + default: + super.handleMessage(msg); + } + } + }; + final FragmentManagerImpl mFragments = new FragmentManagerImpl(); + + ActionBar mActionBar; + boolean mIsActionBarImplAttached; + long mWindowFlags = 0; + + final MenuBuilder mSupportMenu; + final MenuBuilder.Callback mSupportMenuCallback = new MenuBuilder.Callback() { + @Override + public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { + return FragmentActivity.this.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item); + } + }; + + boolean mCreated; + boolean mResumed; + boolean mStopped; + boolean mReallyStopped; + boolean mRetaining; + + boolean mOptionsMenuInvalidated; + boolean mOptionsMenuCreateResult; + + boolean mCheckedForLoaderManager; + boolean mLoadersStarted; + HCSparseArray mAllLoaderManagers; + LoaderManagerImpl mLoaderManager; + + static final class NonConfigurationInstances { + Object activity; + Object custom; + HashMap children; + ArrayList fragments; + HCSparseArray loaders; + } + + static class FragmentTag { + public static final int[] Fragment = { + 0x01010003, 0x010100d0, 0x010100d1 + }; + public static final int Fragment_id = 1; + public static final int Fragment_name = 0; + public static final int Fragment_tag = 2; + } + + + + public FragmentActivity() { + if (IS_HONEYCOMB) { + mActionBar = ActionBarWrapper.createFor(this); + mSupportMenu = null; //Everything should be done natively + } else { + mSupportMenu = new MenuBuilder(this); + mSupportMenu.setCallback(mSupportMenuCallback); + } + } + + @Override + public SupportActivity.InternalCallbacks getInternalCallbacks() { + return mInternalCallbacks; + } + + @Override + public Activity asActivity() { + return this; + } + + protected void ensureSupportActionBarAttached() { + if (IS_HONEYCOMB || mIsActionBarImplAttached) { + return; + } + if (!isChild()) { + if ((mWindowFlags & WINDOW_FLAG_ACTION_BAR) == WINDOW_FLAG_ACTION_BAR) { + if ((mWindowFlags & WINDOW_FLAG_ACTION_BAR_OVERLAY) == WINDOW_FLAG_ACTION_BAR_OVERLAY) { + super.setContentView(R.layout.abs__screen_action_bar_overlay); + } else { + super.setContentView(R.layout.abs__screen_action_bar); + } + + mActionBar = new ActionBarImpl(this); + ((ActionBarImpl)mActionBar).init(); + + final boolean textEnabled = ((mWindowFlags & WINDOW_FLAG_ACTION_BAR_ITEM_TEXT) == WINDOW_FLAG_ACTION_BAR_ITEM_TEXT); + mSupportMenu.setShowsActionItemText(textEnabled); + + if ((mWindowFlags & WINDOW_FLAG_INDETERMINANTE_PROGRESS) == WINDOW_FLAG_INDETERMINANTE_PROGRESS) { + ((ActionBarImpl)mActionBar).setProgressBarIndeterminateVisibility(false); + } + + //TODO set other flags + } else { + if ((mWindowFlags & WINDOW_FLAG_INDETERMINANTE_PROGRESS) == WINDOW_FLAG_INDETERMINANTE_PROGRESS) { + super.requestWindowFeature((int)Window.FEATURE_INDETERMINATE_PROGRESS); + } + super.setContentView(R.layout.abs__screen_simple); + } + } + + invalidateOptionsMenu(); + mIsActionBarImplAttached = true; + } + + // ------------------------------------------------------------------------ + // HOOKS INTO ACTIVITY + // ------------------------------------------------------------------------ + + /** + * Enable extended window features. + * + * @param featureId The desired feature as defined in + * {@link android.support.v4.view.Window}. + * @return Returns {@code true} if the requested feature is supported and + * now enabled. + */ + @Override + public boolean requestWindowFeature(long featureId) { + if (!IS_HONEYCOMB) { + switch ((int)featureId) { + case (int)Window.FEATURE_ACTION_BAR: + case (int)Window.FEATURE_ACTION_BAR_ITEM_TEXT: + case (int)Window.FEATURE_ACTION_BAR_OVERLAY: + case (int)Window.FEATURE_ACTION_MODE_OVERLAY: + case (int)Window.FEATURE_INDETERMINATE_PROGRESS: + mWindowFlags |= (1 << featureId); + return true; + } + } + return super.requestWindowFeature((int)featureId); + } + + @Override + public android.view.MenuInflater getMenuInflater() { + if (IS_HONEYCOMB) { + if (DEBUG) Log.d(TAG, "getMenuInflater(): Wrapping native inflater."); + + //Wrap the native inflater so it can unwrap the native menu first + return new MenuInflaterWrapper(this, super.getMenuInflater()); + } + + if (DEBUG) Log.d(TAG, "getMenuInflater(): Returning support inflater."); + + //Use our custom menu inflater + return new MenuInflater(this, super.getMenuInflater()); + } + + @Override + public void setContentView(int layoutResId) { + ensureSupportActionBarAttached(); + if (IS_HONEYCOMB || isChild()) { + super.setContentView(layoutResId); + } else { + FrameLayout contentView = (FrameLayout)findViewById(R.id.abs__content); + contentView.removeAllViews(); + getLayoutInflater().inflate(layoutResId, contentView, true); + } + } + + @Override + public void setContentView(View view, LayoutParams params) { + ensureSupportActionBarAttached(); + if (IS_HONEYCOMB || isChild()) { + super.setContentView(view, params); + } else { + FrameLayout contentView = (FrameLayout)findViewById(R.id.abs__content); + contentView.removeAllViews(); + contentView.addView(view, params); + } + } + + @Override + public void setContentView(View view) { + ensureSupportActionBarAttached(); + if (IS_HONEYCOMB || isChild()) { + super.setContentView(view); + } else { + FrameLayout contentView = (FrameLayout)findViewById(R.id.abs__content); + contentView.removeAllViews(); + contentView.addView(view); + } + } + + @Override + public void setTitle(CharSequence title) { + if (IS_HONEYCOMB || (getSupportActionBar() == null)) { + super.setTitle(title); + } else { + getSupportActionBar().setTitle(title); + } + } + + @Override + public void setTitle(int titleId) { + if (IS_HONEYCOMB || (getSupportActionBar() == null)) { + super.setTitle(titleId); + } else { + getSupportActionBar().setTitle(titleId); + } + } + + /** + * Dispatch incoming result to the correct fragment. + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + int index = requestCode>>16; + if (index != 0) { + index--; + if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) { + Log.w(TAG, "Activity result fragment index out of range: 0x" + + Integer.toHexString(requestCode)); + return; + } + Fragment frag = mFragments.mActive.get(index); + if (frag == null) { + Log.w(TAG, "Activity result no fragment exists for index: 0x" + + Integer.toHexString(requestCode)); + } else { + frag.onActivityResult(requestCode&0xffff, resultCode, data); + } + return; + } + + super.onActivityResult(requestCode, resultCode, data); + } + + @Override + protected void onApplyThemeResource(Theme theme, int resid, boolean first) { + TypedArray attrs = theme.obtainStyledAttributes(resid, R.styleable.SherlockTheme); + + final boolean actionBar = attrs.getBoolean(R.styleable.SherlockTheme_windowActionBar, false); + mWindowFlags |= actionBar ? WINDOW_FLAG_ACTION_BAR : 0; + + final boolean actionModeOverlay = attrs.getBoolean(R.styleable.SherlockTheme_windowActionModeOverlay, false); + mWindowFlags |= actionModeOverlay ? WINDOW_FLAG_ACTION_MODE_OVERLAY : 0; + + attrs.recycle(); + super.onApplyThemeResource(theme, resid, first); + } + + /** + * Take care of popping the fragment back stack or finishing the activity + * as appropriate. + */ + @Override + public void onBackPressed() { + if (!mFragments.popBackStackImmediate()) { + finish(); + } + } + + /** + * Dispatch configuration change to all fragments. + */ + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mFragments.dispatchConfigurationChanged(newConfig); + } + + /** + * Perform initialization of all fragments and loaders. + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + mFragments.attachActivity(this); + // Old versions of the platform didn't do this! + if (getLayoutInflater().getFactory() == null) { + getLayoutInflater().setFactory(this); + } + + super.onCreate(savedInstanceState); + + NonConfigurationInstances nc = (NonConfigurationInstances) + getLastNonConfigurationInstance(); + if (nc != null) { + mAllLoaderManagers = nc.loaders; + } + if (savedInstanceState != null) { + Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); + mFragments.restoreAllState(p, nc != null ? nc.fragments : null); + } + mFragments.dispatchCreate(); + } + + /** + *

Initialize the contents of the Activity's standard options menu. You + * should place your menu items in to menu.

+ * + *

The default implementation populates the menu with standard system + * menu items. These are placed in the {@link Menu.CATEGORY_SYSTEM} group + * so that they will be correctly ordered with application-defined menu + * items. Deriving classes should always call through to the base + * implementation.

+ * + *

You can safely hold on to menu (and any items created from it), + * making modifications to it as desired, until the next time + * {@code onCreateOptionsMenu()} is called.

+ * + *

When you add items to the menu, you can implement the Activity's + * {@link #onOptionsItemSelected(MenuItem)} method to handle them + * there.

+ * + * @param menu The options menu in which you place your items. + * @return You must return true for the menu to be displayed; if you return + * false it will not be shown. + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu(Menu): Returning " + menu.hasVisibleItems()); + return menu.hasVisibleItems(); + } + + @Override + public final boolean onCreateOptionsMenu(android.view.Menu menu) { + // Prior to Honeycomb, the framework can't invalidate the options + // menu, so we must always say we have one in case the app later + // invalidates it and needs to have it shown. + boolean result = true; + + if (IS_HONEYCOMB) { + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu(android.view.Menu): Calling support method with wrapped native menu."); + MenuWrapper wrapped = new MenuWrapper(menu); + result = onCreateOptionsMenu(wrapped); + result |= mFragments.dispatchCreateOptionsMenu(wrapped, getMenuInflater()); + } + + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu(android.view.Menu): Returning " + result); + return result; + } + + /** + * Add support for inflating the <fragment> tag. + */ + @Override + public View onCreateView(String name, Context context, AttributeSet attrs) { + if (!"fragment".equals(name)) { + return super.onCreateView(name, context, attrs); + } + + String fname = attrs.getAttributeValue(null, "class"); + TypedArray a = context.obtainStyledAttributes(attrs, FragmentTag.Fragment); + if (fname == null) { + fname = a.getString(FragmentTag.Fragment_name); + } + int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID); + String tag = a.getString(FragmentTag.Fragment_tag); + a.recycle(); + + View parent = null; // NOTE: no way to get parent pre-Honeycomb. + int containerId = parent != null ? parent.getId() : 0; + if (containerId == View.NO_ID && id == View.NO_ID && tag == null) { + throw new IllegalArgumentException(attrs.getPositionDescription() + + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname); + } + + // If we restored from a previous state, we may already have + // instantiated this fragment from the state and should use + // that instance instead of making a new one. + Fragment fragment = id != View.NO_ID ? mFragments.findFragmentById(id) : null; + if (fragment == null && tag != null) { + fragment = mFragments.findFragmentByTag(tag); + } + if (fragment == null && containerId != View.NO_ID) { + fragment = mFragments.findFragmentById(containerId); + } + + if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x" + + Integer.toHexString(id) + " fname=" + fname + + " existing=" + fragment); + if (fragment == null) { + fragment = Fragment.instantiate(this, fname); + fragment.mFromLayout = true; + fragment.mFragmentId = id != 0 ? id : containerId; + fragment.mContainerId = containerId; + fragment.mTag = tag; + fragment.mInLayout = true; + fragment.mFragmentManager = mFragments; + fragment.onInflate(this, attrs, fragment.mSavedFragmentState); + mFragments.addFragment(fragment, true); + + } else if (fragment.mInLayout) { + // A fragment already exists and it is not one we restored from + // previous state. + throw new IllegalArgumentException(attrs.getPositionDescription() + + ": Duplicate id 0x" + Integer.toHexString(id) + + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId) + + " with another fragment for " + fname); + } else { + // This fragment was retained from a previous instance; get it + // going now. + fragment.mInLayout = true; + // If this fragment is newly instantiated (either right now, or + // from last saved state), then give it the attributes to + // initialize itself. + if (!fragment.mRetaining) { + fragment.onInflate(this, attrs, fragment.mSavedFragmentState); + } + mFragments.moveToState(fragment); + } + + if (fragment.mView == null) { + throw new IllegalStateException("Fragment " + fname + + " did not create a view."); + } + if (id != 0) { + fragment.mView.setId(id); + } + if (fragment.mView.getTag() == null) { + fragment.mView.setTag(tag); + } + return fragment.mView; + } + + @Override + public void invalidateOptionsMenu() { + if (DEBUG) Log.d(TAG, "supportInvalidateOptionsMenu(): Invalidating menu."); + + if (IS_HONEYCOMB) { + HoneycombInvalidateOptionsMenu.invoke(this); + } else { + mSupportMenu.clear(); + + mOptionsMenuCreateResult = onCreateOptionsMenu(mSupportMenu); + mOptionsMenuCreateResult |= mFragments.dispatchCreateOptionsMenu(mSupportMenu, getMenuInflater()); + + if (getSupportActionBar() != null) { + if (onPrepareOptionsMenu(mSupportMenu)) { + mFragments.dispatchPrepareOptionsMenu(mSupportMenu); + } + + //Since we now know we are using a custom action bar, perform the + //inflation callback to allow it to display any items it wants. + ((ActionBarImpl)mActionBar).onMenuInflated(mSupportMenu); + } + + // Whoops, older platform... we'll use a hack, to manually rebuild + // the options menu the next time it is prepared. + mOptionsMenuInvalidated = true; + } + } + + private static final class HoneycombInvalidateOptionsMenu { + static void invoke(Activity activity) { + activity.getWindow().invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); + } + } + + /** + * Destroy all fragments and loaders. + */ + @Override + protected void onDestroy() { + super.onDestroy(); + + doReallyStop(false); + + mFragments.dispatchDestroy(); + if (mLoaderManager != null) { + mLoaderManager.doDestroy(); + } + } + + /** + * Take care of calling onBackPressed() for pre-Eclair platforms. + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (android.os.Build.VERSION.SDK_INT < 5 /* ECLAIR */ + && keyCode == KeyEvent.KEYCODE_BACK + && event.getRepeatCount() == 0) { + // Take care of calling this method on earlier versions of + // the platform where it doesn't exist. + onBackPressed(); + return true; + } + + return super.onKeyDown(keyCode, event); + } + + /** + * Dispatch onLowMemory() to all fragments. + */ + @Override + public void onLowMemory() { + super.onLowMemory(); + mFragments.dispatchLowMemory(); + } + + /** + * Dispatch context and options menu to fragments. + */ + @Override + public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) { + if (super.onMenuItemSelected(featureId, item)) { + return true; + } + + switch (featureId) { + case Window.FEATURE_OPTIONS_PANEL: + return mFragments.dispatchOptionsItemSelected(new MenuItemWrapper(item)); + + case Window.FEATURE_CONTEXT_MENU: + return mFragments.dispatchContextItemSelected(new MenuItemWrapper(item)); + + default: + return false; + } + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + if (onOptionsItemSelected(item)) { + return true; + } + + switch (featureId) { + case Window.FEATURE_OPTIONS_PANEL: + return mFragments.dispatchOptionsItemSelected(item); + + case Window.FEATURE_CONTEXT_MENU: + return mFragments.dispatchContextItemSelected(item); + + default: + return false; + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return super.onOptionsItemSelected(item); + } + + @Override + public final boolean onOptionsItemSelected(android.view.MenuItem item) { + return onOptionsItemSelected(new MenuItemWrapper(item)); + } + + /** + * Call onOptionsMenuClosed() on fragments. + */ + @Override + public void onPanelClosed(int featureId, android.view.Menu menu) { + switch (featureId) { + case Window.FEATURE_OPTIONS_PANEL: + mFragments.dispatchOptionsMenuClosed(new MenuWrapper(menu)); + + if (!IS_HONEYCOMB && (getSupportActionBar() != null)) { + if (DEBUG) Log.d(TAG, "onPanelClosed(int, android.view.Menu): Dispatch menu visibility false to custom action bar."); + ((ActionBarImpl)mActionBar).onMenuVisibilityChanged(false); + } + break; + } + super.onPanelClosed(featureId, menu); + } + + /** + * Dispatch onPause() to fragments. + */ + @Override + protected void onPause() { + super.onPause(); + mResumed = false; + if (mHandler.hasMessages(MSG_RESUME_PENDING)) { + mHandler.removeMessages(MSG_RESUME_PENDING); + mFragments.dispatchResume(); + } + mFragments.dispatchPause(); + } + + /** + * Dispatch onResume() to fragments. + */ + @Override + protected void onResume() { + super.onResume(); + mHandler.sendEmptyMessage(MSG_RESUME_PENDING); + mResumed = true; + mFragments.execPendingActions(); + } + + /** + * Dispatch onResume() to fragments. + */ + @Override + protected void onPostResume() { + super.onPostResume(); + mHandler.removeMessages(MSG_RESUME_PENDING); + mFragments.dispatchResume(); + mFragments.execPendingActions(); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + boolean result = menu.hasVisibleItems(); + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(Menu): Returning " + result); + return result; + } + + @Override + public final boolean onPrepareOptionsMenu(android.view.Menu menu) { + boolean result = super.onPrepareOptionsMenu(menu); + + if (!IS_HONEYCOMB) { + if (DEBUG) { + Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): mOptionsMenuCreateResult = " + mOptionsMenuCreateResult); + Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): mOptionsMenuInvalidated = " + mOptionsMenuInvalidated); + } + + boolean prepareResult = true; + if (mOptionsMenuCreateResult) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Calling support method with custom menu."); + prepareResult = onPrepareOptionsMenu(mSupportMenu); + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Support method result returned " + prepareResult); + if (prepareResult) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Dispatching fragment method with custom menu."); + mFragments.dispatchPrepareOptionsMenu(mSupportMenu); + } + } + + if (mOptionsMenuInvalidated) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Clearing existing options menu."); + menu.clear(); + mOptionsMenuInvalidated = false; + + if (mOptionsMenuCreateResult && prepareResult) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Adding any action items that are not displayed on the action bar."); + //Only add items that have not already been added to our custom + //action bar implementation + for (MenuItemImpl item : mSupportMenu.getItems()) { + if (!item.isShownOnActionBar()) { + item.addTo(menu); + } + } + } + } + + if (mOptionsMenuCreateResult && prepareResult && menu.hasVisibleItems()) { + if (getSupportActionBar() != null) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Dispatch menu visibility true to custom action bar."); + ((ActionBarImpl)mActionBar).onMenuVisibilityChanged(true); + } + result = true; + } + } else { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Calling support method with wrapped native menu."); + final MenuWrapper wrappedMenu = new MenuWrapper(menu); + result = onPrepareOptionsMenu(wrappedMenu); + if (result) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Dispatching fragment method with wrapped native menu."); + mFragments.dispatchPrepareOptionsMenu(wrappedMenu); + } + } + + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Returning " + result); + return result; + } + + /** + * Cause this Activity to be recreated with a new instance. This results in + * essentially the same flow as when the Activity is created due to a + * configuration change -- the current instance will go through its + * lifecycle to onDestroy() and a new instance then created after it. + */ + @Override + public void recreate() { + //This SUCKS! Figure out a way to call the super method and support Android 1.6 + /* + if (IS_HONEYCOMB) { + super.recreate(); + } else { + */ + final Intent intent = getIntent(); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + + startActivity(intent); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) { + OverridePendingTransition.invoke(this); + } + + finish(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) { + OverridePendingTransition.invoke(this); + } + /* + } + */ + } + + private static final class OverridePendingTransition { + static void invoke(Activity activity) { + activity.overridePendingTransition(0, 0); + } + } + + /** + * Retain all appropriate fragment and loader state. You can NOT + * override this yourself! Use {@link #onRetainCustomNonConfigurationInstance()} + * if you want to retain your own state. + */ + @Override + public final Object onRetainNonConfigurationInstance() { + if (mStopped) { + doReallyStop(true); + } + + Object custom = onRetainCustomNonConfigurationInstance(); + + ArrayList fragments = mFragments.retainNonConfig(); + boolean retainLoaders = false; + if (mAllLoaderManagers != null) { + // prune out any loader managers that were already stopped and so + // have nothing useful to retain. + for (int i=mAllLoaderManagers.size()-1; i>=0; i--) { + LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i); + if (lm.mRetaining) { + retainLoaders = true; + } else { + lm.doDestroy(); + mAllLoaderManagers.removeAt(i); + } + } + } + if (fragments == null && !retainLoaders && custom == null) { + return null; + } + + NonConfigurationInstances nci = new NonConfigurationInstances(); + nci.activity = null; + nci.custom = custom; + nci.children = null; + nci.fragments = fragments; + nci.loaders = mAllLoaderManagers; + return nci; + } + + /** + * Save all appropriate fragment state. + */ + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + Parcelable p = mFragments.saveAllState(); + if (p != null) { + outState.putParcelable(FRAGMENTS_TAG, p); + } + } + + /** + * Dispatch onStart() to all fragments. Ensure any created loaders are + * now started. + */ + @Override + protected void onStart() { + super.onStart(); + + mStopped = false; + mReallyStopped = false; + mHandler.removeMessages(MSG_REALLY_STOPPED); + + if (!mCreated) { + mCreated = true; + ensureSupportActionBarAttached(); //Needed for retained fragments + mFragments.dispatchActivityCreated(); + } + + mFragments.noteStateNotSaved(); + mFragments.execPendingActions(); + + if (!mLoadersStarted) { + mLoadersStarted = true; + if (mLoaderManager != null) { + mLoaderManager.doStart(); + } else if (!mCheckedForLoaderManager) { + mLoaderManager = getLoaderManager(-1, mLoadersStarted, false); + } + mCheckedForLoaderManager = true; + } + // NOTE: HC onStart goes here. + + mFragments.dispatchStart(); + if (mAllLoaderManagers != null) { + for (int i=mAllLoaderManagers.size()-1; i>=0; i--) { + LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i); + lm.finishRetain(); + lm.doReportStart(); + } + } + } + + /** + * Dispatch onStop() to all fragments. Ensure all loaders are stopped. + */ + @Override + protected void onStop() { + super.onStop(); + + mStopped = true; + mHandler.sendEmptyMessage(MSG_REALLY_STOPPED); + + mFragments.dispatchStop(); + } + + /** + *

Sets the visibility of the indeterminate progress bar in the + * title.

+ * + *

In order for the progress bar to be shown, the feature must be + * requested via {@link #requestWindowFeature(long)}.

+ * + *

This method must be used instead of + * {@link #setProgressBarIndeterminateVisibility(boolean)} for + * ActionBarSherlock. Pass {@link Boolean.TRUE} or + * {@link Boolean.FALSE} to ensure the appropriate one is called.

+ * + * @param visible Whether to show the progress bars in the title. + */ + @Override + public void setProgressBarIndeterminateVisibility(Boolean visible) { + if (IS_HONEYCOMB || (getSupportActionBar() == null)) { + super.setProgressBarIndeterminateVisibility(visible); + } else if ((mWindowFlags & WINDOW_FLAG_INDETERMINANTE_PROGRESS) == WINDOW_FLAG_INDETERMINANTE_PROGRESS) { + ((ActionBarImpl)mActionBar).setProgressBarIndeterminateVisibility(visible); + } + } + + // ------------------------------------------------------------------------ + // NEW METHODS + // ------------------------------------------------------------------------ + + /** + * Use this instead of {@link #onRetainNonConfigurationInstance()}. + * Retrieve later with {@link #getLastCustomNonConfigurationInstance()}. + */ + public Object onRetainCustomNonConfigurationInstance() { + return null; + } + + /** + * Return the value previously returned from + * {@link #onRetainCustomNonConfigurationInstance()}. + */ + public Object getLastCustomNonConfigurationInstance() { + NonConfigurationInstances nc = (NonConfigurationInstances) + getLastNonConfigurationInstance(); + return nc != null ? nc.custom : null; + } + + /** + * @deprecated Use {@link invalidateOptionsMenu}. + */ + @Deprecated + void supportInvalidateOptionsMenu() { + invalidateOptionsMenu(); + } + + /** + * Print the Activity's state into the given stream. This gets invoked if + * you run "adb shell dumpsys activity ". + * + * @param prefix Desired prefix to prepend at each line of output. + * @param fd The raw file descriptor that the dump is being sent to. + * @param writer The PrintWriter to which you should dump your state. This will be + * closed for you after you return. + * @param args additional arguments to the dump request. + */ + @Override + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + if (IS_HONEYCOMB) { + //This can only work if we can call the super-class impl. :/ + //ActivityCompatHoneycomb.dump(this, prefix, fd, writer, args); + } + writer.print(prefix); writer.print("Local FragmentActivity "); + writer.print(Integer.toHexString(System.identityHashCode(this))); + writer.println(" State:"); + String innerPrefix = prefix + " "; + writer.print(innerPrefix); writer.print("mCreated="); + writer.print(mCreated); writer.print("mResumed="); + writer.print(mResumed); writer.print(" mStopped="); + writer.print(mStopped); writer.print(" mReallyStopped="); + writer.println(mReallyStopped); + writer.print(innerPrefix); writer.print("mLoadersStarted="); + writer.println(mLoadersStarted); + if (mLoaderManager != null) { + writer.print(prefix); writer.print("Loader Manager "); + writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager))); + writer.println(":"); + mLoaderManager.dump(prefix + " ", fd, writer, args); + } + mFragments.dump(prefix, fd, writer, args); + } + + void doReallyStop(boolean retaining) { + if (!mReallyStopped) { + mReallyStopped = true; + mRetaining = retaining; + mHandler.removeMessages(MSG_REALLY_STOPPED); + onReallyStop(); + } + } + + /** + * Pre-HC, we didn't have a way to determine whether an activity was + * being stopped for a config change or not until we saw + * onRetainNonConfigurationInstance() called after onStop(). However + * we need to know this, to know whether to retain fragments. This will + * tell us what we need to know. + */ + void onReallyStop() { + if (mLoadersStarted) { + mLoadersStarted = false; + if (mLoaderManager != null) { + if (!mRetaining) { + mLoaderManager.doStop(); + } else { + mLoaderManager.doRetain(); + } + } + } + + mFragments.dispatchReallyStop(); + } + + // ------------------------------------------------------------------------ + // ACTION BAR AND ACTION MODE SUPPORT + // ------------------------------------------------------------------------ + + /** + * Retrieve a reference to this activity's action bar handler. + * + * @return The handler for the appropriate action bar, or null. + */ + @Override + public ActionBar getSupportActionBar() { + return (mActionBar != null) ? mActionBar.getPublicInstance() : null; + } + + /** + * Notifies the activity that an action mode has finished. Activity + * subclasses overriding this method should call the superclass + * implementation. + * + * @param mode The action mode that just finished. + */ + @Override + public void onActionModeFinished(ActionMode mode) { + } + + /** + * Notifies the Activity that an action mode has been started. Activity + * subclasses overriding this method should call the superclass + * implementation. + * + * @param mode The new action mode. + */ + @Override + public void onActionModeStarted(ActionMode mode) { + } + + /** + *

Give the Activity a chance to control the UI for an action mode + * requested by the system.

+ * + *

Note: If you are looking for a notification callback that an action + * mode has been started for this activity, see + * {@link #onActionModeStarted(ActionMode)}.

+ * + * @param callback The callback that should control the new action mode + * @return The new action mode, or null if the activity does not want to + * provide special handling for this action mode. (It will be handled by the + * system.) + */ + @Override + public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { + return null; + } + + /** + * 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 cancelled + * @see android.support.v4.view.ActionMode + */ + @Override + public final ActionMode startActionMode(final ActionMode.Callback callback) { + //Give the activity override a chance to handle the action mode + ActionMode actionMode = onWindowStartingActionMode(callback); + + if (actionMode == null) { + //If the activity did not handle, send to action bar for platform- + //specific implementation + actionMode = mActionBar.startActionMode(callback); + } + if (actionMode != null) { + //Send the activity callback that our action mode was started + onActionModeStarted(actionMode); + } + + //Return to the caller + return actionMode; + } + + // ------------------------------------------------------------------------ + // FRAGMENT SUPPORT + // ------------------------------------------------------------------------ + + /** + * Called when a fragment is attached to the activity. + */ + @Override + public void onAttachFragment(Fragment fragment) { + } + + /** + * Return the FragmentManager for interacting with fragments associated + * with this activity. + */ + @Override + public FragmentManager getSupportFragmentManager() { + return mFragments; + } + + /** + * Modifies the standard behavior to allow results to be delivered to fragments. + * This imposes a restriction that requestCode be <= 0xffff. + */ + @Override + public void startActivityForResult(Intent intent, int requestCode) { + if (requestCode != -1 && (requestCode&0xffff0000) != 0) { + throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); + } + super.startActivityForResult(intent, requestCode); + } + + /** + * Called by Fragment.startActivityForResult() to implement its behavior. + */ + @Override + public void startActivityFromFragment(Fragment fragment, Intent intent, + int requestCode) { + if (requestCode == -1) { + super.startActivityForResult(intent, -1); + return; + } + if ((requestCode&0xffff0000) != 0) { + throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); + } + super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff)); + } + + void invalidateSupportFragmentIndex(int index) { + //Log.v(TAG, "invalidateFragmentIndex: index=" + index); + if (mAllLoaderManagers != null) { + LoaderManagerImpl lm = mAllLoaderManagers.get(index); + if (lm != null && !lm.mRetaining) { + lm.doDestroy(); + mAllLoaderManagers.remove(index); + } + } + } + + // ------------------------------------------------------------------------ + // LOADER SUPPORT + // ------------------------------------------------------------------------ + + /** + * Return the LoaderManager for this fragment, creating it if needed. + */ + @Override + public LoaderManager getSupportLoaderManager() { + if (mLoaderManager != null) { + return mLoaderManager; + } + mCheckedForLoaderManager = true; + mLoaderManager = getLoaderManager(-1, mLoadersStarted, true); + return mLoaderManager; + } + + LoaderManagerImpl getLoaderManager(int index, boolean started, boolean create) { + if (mAllLoaderManagers == null) { + mAllLoaderManagers = new HCSparseArray(); + } + LoaderManagerImpl lm = mAllLoaderManagers.get(index); + if (lm == null) { + if (create) { + lm = new LoaderManagerImpl(this, started); + mAllLoaderManagers.put(index, lm); + } + } else { + lm.updateActivity(this); + } + return lm; + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/app/FragmentManager.java b/actionbarsherlock/library/src/android/support/v4/app/FragmentManager.java new file mode 100644 index 000000000..3d35d9eb8 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/FragmentManager.java @@ -0,0 +1,1936 @@ +/* + * 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 android.support.v4.app; + +import android.content.Context; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v4.util.DebugUtils; +import android.support.v4.util.LogWriter; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.util.Log; +import android.util.SparseArray; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationSet; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.ScaleAnimation; +import android.view.animation.Animation.AnimationListener; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; +import com.actionbarsherlock.R; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Static library support version of the framework's {@link android.app.FragmentManager}. + * Used to write apps that run on platforms prior to Android 3.0. When running + * on Android 3.0 or above, this implementation is still used; it does not try + * to switch to the framework's implementation. See the framework SDK + * documentation for a class overview. + * + *

Your activity must derive from {@link FragmentActivity} to use this. + */ +public abstract class FragmentManager { + /** + * Representation of an entry on the fragment back stack, as created + * with {@link FragmentTransaction#addToBackStack(String) + * FragmentTransaction.addToBackStack()}. Entries can later be + * retrieved with {@link FragmentManager#getBackStackEntryAt(int) + * FragmentManager.getBackStackEntry()}. + * + *

Note that you should never hold on to a BackStackEntry object; + * the identifier as returned by {@link #getId} is the only thing that + * will be persisted across activity instances. + */ + public interface BackStackEntry { + /** + * Return the unique identifier for the entry. This is the only + * representation of the entry that will persist across activity + * instances. + */ + public int getId(); + + /** + * Get the name that was supplied to + * {@link FragmentTransaction#addToBackStack(String) + * FragmentTransaction.addToBackStack(String)} when creating this entry. + */ + public String getName(); + + /** + * Return the full bread crumb title resource identifier for the entry, + * or 0 if it does not have one. + */ + public int getBreadCrumbTitleRes(); + + /** + * Return the short bread crumb title resource identifier for the entry, + * or 0 if it does not have one. + */ + public int getBreadCrumbShortTitleRes(); + + /** + * Return the full bread crumb title for the entry, or null if it + * does not have one. + */ + public CharSequence getBreadCrumbTitle(); + + /** + * Return the short bread crumb title for the entry, or null if it + * does not have one. + */ + public CharSequence getBreadCrumbShortTitle(); + } + + /** + * Interface to watch for changes to the back stack. + */ + public interface OnBackStackChangedListener { + /** + * Called whenever the contents of the back stack change. + */ + public void onBackStackChanged(); + } + + /** + * Start a series of edit operations on the Fragments associated with + * this FragmentManager. + * + *

Note: A fragment transaction can only be created/committed prior + * to an activity saving its state. If you try to commit a transaction + * after {@link FragmentActivity#onSaveInstanceState FragmentActivity.onSaveInstanceState()} + * (and prior to a following {@link FragmentActivity#onStart FragmentActivity.onStart} + * or {@link FragmentActivity#onResume FragmentActivity.onResume()}, you will get an error. + * This is because the framework takes care of saving your current fragments + * in the state, and if changes are made after the state is saved then they + * will be lost.

+ */ + public abstract FragmentTransaction beginTransaction(); + + /** @hide -- remove once prebuilts are in. */ + @Deprecated + public FragmentTransaction openTransaction() { + return beginTransaction(); + } + + /** + * After a {@link FragmentTransaction} is committed with + * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it + * is scheduled to be executed asynchronously on the process's main thread. + * If you want to immediately executing any such pending operations, you + * can call this function (only from the main thread) to do so. Note that + * all callbacks and other related behavior will be done from within this + * call, so be careful about where this is called from. + * + * @return Returns true if there were any pending transactions to be + * executed. + */ + public abstract boolean executePendingTransactions(); + + /** + * Finds a fragment that was identified by the given id either when inflated + * from XML or as the container ID when added in a transaction. This first + * searches through fragments that are currently added to the manager's + * activity; if no such fragment is found, then all fragments currently + * on the back stack associated with this ID are searched. + * @return The fragment if found or null otherwise. + */ + public abstract Fragment findFragmentById(int id); + + /** + * Finds a fragment that was identified by the given tag either when inflated + * from XML or as supplied when added in a transaction. This first + * searches through fragments that are currently added to the manager's + * activity; if no such fragment is found, then all fragments currently + * on the back stack are searched. + * @return The fragment if found or null otherwise. + */ + public abstract Fragment findFragmentByTag(String tag); + + /** + * Flag for {@link #popBackStack(String, int)} + * and {@link #popBackStack(int, int)}: If set, and the name or ID of + * a back stack entry has been supplied, then all matching entries will + * be consumed until one that doesn't match is found or the bottom of + * the stack is reached. Otherwise, all entries up to but not including that entry + * will be removed. + */ + public static final int POP_BACK_STACK_INCLUSIVE = 1<<0; + + /** + * Pop the top state off the back stack. Returns true if there was one + * to pop, else false. This function is asynchronous -- it enqueues the + * request to pop, but the action will not be performed until the application + * returns to its event loop. + */ + public abstract void popBackStack(); + + /** + * Like {@link #popBackStack()}, but performs the operation immediately + * inside of the call. This is like calling {@link #executePendingTransactions()} + * afterwards. + * @return Returns true if there was something popped, else false. + */ + public abstract boolean popBackStackImmediate(); + + /** + * Pop the last fragment transition from the manager's fragment + * back stack. If there is nothing to pop, false is returned. + * This function is asynchronous -- it enqueues the + * request to pop, but the action will not be performed until the application + * returns to its event loop. + * + * @param name If non-null, this is the name of a previous back state + * to look for; if found, all states up to that state will be popped. The + * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether + * the named state itself is popped. If null, only the top state is popped. + * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. + */ + public abstract void popBackStack(String name, int flags); + + /** + * Like {@link #popBackStack(String, int)}, but performs the operation immediately + * inside of the call. This is like calling {@link #executePendingTransactions()} + * afterwards. + * @return Returns true if there was something popped, else false. + */ + public abstract boolean popBackStackImmediate(String name, int flags); + + /** + * Pop all back stack states up to the one with the given identifier. + * This function is asynchronous -- it enqueues the + * request to pop, but the action will not be performed until the application + * returns to its event loop. + * + * @param id Identifier of the stated to be popped. If no identifier exists, + * false is returned. + * The identifier is the number returned by + * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The + * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether + * the named state itself is popped. + * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. + */ + public abstract void popBackStack(int id, int flags); + + /** + * Like {@link #popBackStack(int, int)}, but performs the operation immediately + * inside of the call. This is like calling {@link #executePendingTransactions()} + * afterwards. + * @return Returns true if there was something popped, else false. + */ + public abstract boolean popBackStackImmediate(int id, int flags); + + /** + * Return the number of entries currently in the back stack. + */ + public abstract int getBackStackEntryCount(); + + /** + * Return the BackStackEntry at index index in the back stack; + * entries start index 0 being the bottom of the stack. + */ + public abstract BackStackEntry getBackStackEntryAt(int index); + + /** + * Add a new listener for changes to the fragment back stack. + */ + public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener); + + /** + * Remove a listener that was previously added with + * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}. + */ + public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener); + + /** + * Put a reference to a fragment in a Bundle. This Bundle can be + * persisted as saved state, and when later restoring + * {@link #getFragment(Bundle, String)} will return the current + * instance of the same fragment. + * + * @param bundle The bundle in which to put the fragment reference. + * @param key The name of the entry in the bundle. + * @param fragment The Fragment whose reference is to be stored. + */ + public abstract void putFragment(Bundle bundle, String key, Fragment fragment); + + /** + * Retrieve the current Fragment instance for a reference previously + * placed with {@link #putFragment(Bundle, String, Fragment)}. + * + * @param bundle The bundle from which to retrieve the fragment reference. + * @param key The name of the entry in the bundle. + * @return Returns the current Fragment instance that is associated with + * the given reference. + */ + public abstract Fragment getFragment(Bundle bundle, String key); + + /** + * Save the current instance state of the given Fragment. This can be + * used later when creating a new instance of the Fragment and adding + * it to the fragment manager, to have it create itself to match the + * current state returned here. Note that there are limits on how + * this can be used: + * + *
    + *
  • The Fragment must currently be attached to the FragmentManager. + *
  • A new Fragment created using this saved state must be the same class + * type as the Fragment it was created from. + *
  • The saved state can not contain dependencies on other fragments -- + * that is it can't use {@link #putFragment(Bundle, String, Fragment)} to + * store a fragment reference because that reference may not be valid when + * this saved state is later used. Likewise the Fragment's target and + * result code are not included in this state. + *
+ * + * @param f The Fragment whose state is to be saved. + * @return The generated state. This will be null if there was no + * interesting state created by the fragment. + */ + public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f); + + /** + * Print the FragmentManager's state into the given stream. + * + * @param prefix Text to print at the front of each line. + * @param fd The raw file descriptor that the dump is being sent to. + * @param writer A PrintWriter to which the dump is to be set. + * @param args Additional arguments to the dump request. + */ + public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); + + /** + * Control whether the framework's internal fragment manager debugging + * logs are turned on. If enabled, you will see output in logcat as + * the framework performs fragment operations. + */ + public static void enableDebugLogging(boolean enabled) { + FragmentManagerImpl.DEBUG = enabled; + } +} + +final class FragmentManagerState implements Parcelable { + FragmentState[] mActive; + int[] mAdded; + BackStackState[] mBackStack; + + public FragmentManagerState() { + } + + public FragmentManagerState(Parcel in) { + mActive = in.createTypedArray(FragmentState.CREATOR); + mAdded = in.createIntArray(); + mBackStack = in.createTypedArray(BackStackState.CREATOR); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeTypedArray(mActive, flags); + dest.writeIntArray(mAdded); + dest.writeTypedArray(mBackStack, flags); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public FragmentManagerState createFromParcel(Parcel in) { + return new FragmentManagerState(in); + } + + public FragmentManagerState[] newArray(int size) { + return new FragmentManagerState[size]; + } + }; +} + +/** + * Container for fragments associated with an activity. + */ +final class FragmentManagerImpl extends FragmentManager { + static boolean DEBUG = false; + static final String TAG = "FragmentManager"; + + static final boolean HONEYCOMB = android.os.Build.VERSION.SDK_INT >= 11; + + static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state"; + static final String TARGET_STATE_TAG = "android:target_state"; + static final String VIEW_STATE_TAG = "android:view_state"; + + ArrayList mPendingActions; + Runnable[] mTmpActions; + boolean mExecutingActions; + + ArrayList mActive; + ArrayList mAdded; + ArrayList mAvailIndices; + ArrayList mBackStack; + ArrayList mCreatedMenus; + + // Must be accessed while locked. + ArrayList mBackStackIndices; + ArrayList mAvailBackStackIndices; + + ArrayList mBackStackChangeListeners; + + int mCurState = Fragment.INITIALIZING; + SupportActivity mActivity; + + boolean mNeedMenuInvalidate; + boolean mStateSaved; + boolean mDestroyed; + String mNoTransactionsBecause; + + // Temporary vars for state save and restore. + Bundle mStateBundle = null; + SparseArray mStateArray = null; + + Runnable mExecCommit = new Runnable() { + @Override + public void run() { + execPendingActions(); + } + }; + + @Override + public FragmentTransaction beginTransaction() { + return new BackStackRecord(this); + } + + @Override + public boolean executePendingTransactions() { + return execPendingActions(); + } + + @Override + public void popBackStack() { + enqueueAction(new Runnable() { + @Override public void run() { + popBackStackState(mActivity.getInternalCallbacks().getHandler(), null, -1, 0); + } + }, false); + } + + @Override + public boolean popBackStackImmediate() { + checkStateLoss(); + executePendingTransactions(); + return popBackStackState(mActivity.getInternalCallbacks().getHandler(), null, -1, 0); + } + + @Override + public void popBackStack(final String name, final int flags) { + enqueueAction(new Runnable() { + @Override public void run() { + popBackStackState(mActivity.getInternalCallbacks().getHandler(), name, -1, flags); + } + }, false); + } + + @Override + public boolean popBackStackImmediate(String name, int flags) { + checkStateLoss(); + executePendingTransactions(); + return popBackStackState(mActivity.getInternalCallbacks().getHandler(), name, -1, flags); + } + + @Override + public void popBackStack(final int id, final int flags) { + if (id < 0) { + throw new IllegalArgumentException("Bad id: " + id); + } + enqueueAction(new Runnable() { + @Override public void run() { + popBackStackState(mActivity.getInternalCallbacks().getHandler(), null, id, flags); + } + }, false); + } + + @Override + public boolean popBackStackImmediate(int id, int flags) { + checkStateLoss(); + executePendingTransactions(); + if (id < 0) { + throw new IllegalArgumentException("Bad id: " + id); + } + return popBackStackState(mActivity.getInternalCallbacks().getHandler(), null, id, flags); + } + + @Override + public int getBackStackEntryCount() { + return mBackStack != null ? mBackStack.size() : 0; + } + + @Override + public BackStackEntry getBackStackEntryAt(int index) { + return mBackStack.get(index); + } + + @Override + public void addOnBackStackChangedListener(OnBackStackChangedListener listener) { + if (mBackStackChangeListeners == null) { + mBackStackChangeListeners = new ArrayList(); + } + mBackStackChangeListeners.add(listener); + } + + @Override + public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) { + if (mBackStackChangeListeners != null) { + mBackStackChangeListeners.remove(listener); + } + } + + @Override + public void putFragment(Bundle bundle, String key, Fragment fragment) { + if (fragment.mIndex < 0) { + throw new IllegalStateException("Fragment " + fragment + + " is not currently in the FragmentManager"); + } + bundle.putInt(key, fragment.mIndex); + } + + @Override + public Fragment getFragment(Bundle bundle, String key) { + int index = bundle.getInt(key, -1); + if (index == -1) { + return null; + } + if (index >= mActive.size()) { + throw new IllegalStateException("Fragement no longer exists for key " + + key + ": index " + index); + } + Fragment f = mActive.get(index); + if (f == null) { + throw new IllegalStateException("Fragement no longer exists for key " + + key + ": index " + index); + } + return f; + } + + @Override + public Fragment.SavedState saveFragmentInstanceState(Fragment fragment) { + if (fragment.mIndex < 0) { + throw new IllegalStateException("Fragment " + fragment + + " is not currently in the FragmentManager"); + } + if (fragment.mState > Fragment.INITIALIZING) { + Bundle result = saveFragmentBasicState(fragment); + return result != null ? new Fragment.SavedState(result) : null; + } + return null; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("FragmentManager{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" in "); + DebugUtils.buildShortClassTag(mActivity, sb); + sb.append("}}"); + return sb.toString(); + } + + @Override + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + String innerPrefix = prefix + " "; + + int N; + if (mActive != null) { + N = mActive.size(); + if (N > 0) { + writer.print(prefix); writer.print("Active Fragments in "); + writer.print(Integer.toHexString(System.identityHashCode(this))); + writer.println(":"); + for (int i=0; i 0) { + writer.print(prefix); writer.println("Added Fragments:"); + for (int i=0; i 0) { + writer.print(prefix); writer.println("Fragments Created Menus:"); + for (int i=0; i 0) { + writer.print(prefix); writer.println("Back Stack:"); + for (int i=0; i 0) { + writer.print(prefix); writer.println("Back Stack Indices:"); + for (int i=0; i 0) { + writer.print(prefix); writer.print("mAvailBackStackIndices: "); + writer.println(Arrays.toString(mAvailBackStackIndices.toArray())); + } + } + + if (mPendingActions != null) { + N = mPendingActions.size(); + if (N > 0) { + writer.print(prefix); writer.println("Pending Actions:"); + for (int i=0; i Fragment.CREATED) { + newState = Fragment.CREATED; + } + if (f.mRemoving && newState > f.mState) { + // While removing a fragment, we can't change it to a higher state. + newState = f.mState; + } + + if (f.mState < newState) { + // For fragments that are created from a layout, when restoring from + // state we don't want to allow them to be created until they are + // being reloaded from the layout. + if (f.mFromLayout && !f.mInLayout) { + return; + } + if (f.mAnimatingAway != null) { + // The fragment is currently being animated... but! Now we + // want to move our state back up. Give up on waiting for the + // animation, move to whatever the final state should be once + // the animation is done, and then we can proceed from there. + f.mAnimatingAway = null; + moveToState(f, f.mStateAfterAnimating, 0, 0); + } + switch (f.mState) { + case Fragment.INITIALIZING: + if (DEBUG) Log.v(TAG, "moveto CREATED: " + f); + if (f.mSavedFragmentState != null) { + f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray( + FragmentManagerImpl.VIEW_STATE_TAG); + f.mTarget = getFragment(f.mSavedFragmentState, + FragmentManagerImpl.TARGET_STATE_TAG); + if (f.mTarget != null) { + f.mTargetRequestCode = f.mSavedFragmentState.getInt( + FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0); + } + } + f.mActivity = mActivity; + f.mFragmentManager = mActivity.getInternalCallbacks().getFragments(); + f.mCalled = false; + f.onAttach(mActivity); + if (!f.mCalled) { + throw new SuperNotCalledException("Fragment " + f + + " did not call through to super.onAttach()"); + } + mActivity.onAttachFragment(f); + + if (!f.mRetaining) { + f.mCalled = false; + f.onCreate(f.mSavedFragmentState); + if (!f.mCalled) { + throw new SuperNotCalledException("Fragment " + f + + " did not call through to super.onCreate()"); + } + } + f.mRetaining = false; + if (f.mFromLayout) { + // For fragments that are part of the content view + // layout, we need to instantiate the view immediately + // and the inflater will take care of adding it. + f.mView = f.onCreateView(f.getLayoutInflater(f.mSavedFragmentState), + null, f.mSavedFragmentState); + if (f.mView != null) { + f.mInnerView = f.mView; + f.mView = NoSaveStateFrameLayout.wrap(f.mView); + if (f.mHidden) f.mView.setVisibility(View.GONE); + f.onViewCreated(f.mView, f.mSavedFragmentState); + } else { + f.mInnerView = null; + } + } + case Fragment.CREATED: + if (newState > Fragment.CREATED) { + if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f); + if (!f.mFromLayout) { + ViewGroup container = null; + if (f.mContainerId != 0) { + container = (ViewGroup)mActivity.findViewById(f.mContainerId); + if (container == null && !f.mRestored) { + throw new IllegalArgumentException("No view found for id 0x" + + Integer.toHexString(f.mContainerId) + + " for fragment " + f); + } + } + f.mContainer = container; + f.mView = f.onCreateView(f.getLayoutInflater(f.mSavedFragmentState), + container, f.mSavedFragmentState); + if (f.mView != null) { + f.mInnerView = f.mView; + f.mView = NoSaveStateFrameLayout.wrap(f.mView); + if (container != null) { + Animation anim = loadAnimation(f, transit, true, + transitionStyle); + if (anim != null) { + f.mView.startAnimation(anim); + } + container.addView(f.mView); + } + if (f.mHidden) f.mView.setVisibility(View.GONE); + f.onViewCreated(f.mView, f.mSavedFragmentState); + } else { + f.mInnerView = null; + } + } + + f.mCalled = false; + f.onActivityCreated(f.mSavedFragmentState); + if (!f.mCalled) { + throw new SuperNotCalledException("Fragment " + f + + " did not call through to super.onActivityCreated()"); + } + if (f.mView != null) { + f.restoreViewState(); + } + f.mSavedFragmentState = null; + } + case Fragment.ACTIVITY_CREATED: + case Fragment.STOPPED: + if (newState > Fragment.STOPPED) { + if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); + f.mCalled = false; + f.performStart(); + if (!f.mCalled) { + throw new SuperNotCalledException("Fragment " + f + + " did not call through to super.onStart()"); + } + } + case Fragment.STARTED: + if (newState > Fragment.STARTED) { + if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); + f.mCalled = false; + f.mResumed = true; + f.onResume(); + if (!f.mCalled) { + throw new SuperNotCalledException("Fragment " + f + + " did not call through to super.onResume()"); + } + f.mSavedFragmentState = null; + f.mSavedViewState = null; + } + } + } else if (f.mState > newState) { + switch (f.mState) { + case Fragment.RESUMED: + if (newState < Fragment.RESUMED) { + if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f); + f.mCalled = false; + f.onPause(); + if (!f.mCalled) { + throw new SuperNotCalledException("Fragment " + f + + " did not call through to super.onPause()"); + } + f.mResumed = false; + } + case Fragment.STARTED: + if (newState < Fragment.STARTED) { + if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f); + f.mCalled = false; + f.performStop(); + if (!f.mCalled) { + throw new SuperNotCalledException("Fragment " + f + + " did not call through to super.onStop()"); + } + } + case Fragment.STOPPED: + if (newState < Fragment.STOPPED) { + if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f); + f.performReallyStop(); + } + case Fragment.ACTIVITY_CREATED: + if (newState < Fragment.ACTIVITY_CREATED) { + if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f); + if (f.mView != null) { + // Need to save the current view state if not + // done already. + if (!mActivity.isFinishing() && f.mSavedViewState == null) { + saveFragmentViewState(f); + } + } + f.mCalled = false; + f.performDestroyView(); + if (!f.mCalled) { + throw new SuperNotCalledException("Fragment " + f + + " did not call through to super.onDestroyView()"); + } + if (f.mView != null && f.mContainer != null) { + Animation anim = null; + if (mCurState > Fragment.INITIALIZING && !mDestroyed) { + anim = loadAnimation(f, transit, false, + transitionStyle); + } + if (anim != null) { + final Fragment fragment = f; + f.mAnimatingAway = f.mView; + f.mStateAfterAnimating = newState; + anim.setAnimationListener(new AnimationListener() { + @Override + public void onAnimationEnd(Animation animation) { + if (fragment.mAnimatingAway != null) { + fragment.mAnimatingAway = null; + moveToState(fragment, fragment.mStateAfterAnimating, + 0, 0); + } + } + @Override + public void onAnimationRepeat(Animation animation) { + } + @Override + public void onAnimationStart(Animation animation) { + } + }); + f.mView.startAnimation(anim); + } + f.mContainer.removeView(f.mView); + } + f.mContainer = null; + f.mView = null; + f.mInnerView = null; + } + case Fragment.CREATED: + if (newState < Fragment.CREATED) { + if (mDestroyed) { + if (f.mAnimatingAway != null) { + // The fragment's containing activity is + // being destroyed, but this fragment is + // currently animating away. Stop the + // animation right now -- it is not needed, + // and we can't wait any more on destroying + // the fragment. + View v = f.mAnimatingAway; + f.mAnimatingAway = null; + v.clearAnimation(); + } + } + if (f.mAnimatingAway != null) { + // We are waiting for the fragment's view to finish + // animating away. Just make a note of the state + // the fragment now should move to once the animation + // is done. + f.mStateAfterAnimating = newState; + newState = Fragment.CREATED; + } else { + if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); + if (!f.mRetaining) { + f.mCalled = false; + f.onDestroy(); + if (!f.mCalled) { + throw new SuperNotCalledException("Fragment " + f + + " did not call through to super.onDestroy()"); + } + } + + f.mCalled = false; + f.onDetach(); + if (!f.mCalled) { + throw new SuperNotCalledException("Fragment " + f + + " did not call through to super.onDetach()"); + } + if (!f.mRetaining) { + makeInactive(f); + } else { + f.mActivity = null; + f.mFragmentManager = null; + } + } + } + } + } + + f.mState = newState; + } + + void moveToState(Fragment f) { + moveToState(f, mCurState, 0, 0); + } + + void moveToState(int newState, boolean always) { + moveToState(newState, 0, 0, always); + } + + void moveToState(int newState, int transit, int transitStyle, boolean always) { + if (mActivity == null && newState != Fragment.INITIALIZING) { + throw new IllegalStateException("No activity"); + } + + if (!always && mCurState == newState) { + return; + } + + mCurState = newState; + if (mActive != null) { + for (int i=0; i= 0) { + return; + } + + if (mAvailIndices == null || mAvailIndices.size() <= 0) { + if (mActive == null) { + mActive = new ArrayList(); + } + f.setIndex(mActive.size()); + mActive.add(f); + + } else { + f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1)); + mActive.set(f.mIndex, f); + } + } + + void makeInactive(Fragment f) { + if (f.mIndex < 0) { + return; + } + + if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex); + mActive.set(f.mIndex, null); + if (mAvailIndices == null) { + mAvailIndices = new ArrayList(); + } + mAvailIndices.add(f.mIndex); + mActivity.getInternalCallbacks().invalidateSupportFragmentIndex(f.mIndex); + f.initState(); + } + + public void addFragment(Fragment fragment, boolean moveToStateNow) { + if (mAdded == null) { + mAdded = new ArrayList(); + } + if (DEBUG) Log.v(TAG, "add: " + fragment); + makeActive(fragment); + if (!fragment.mDetached) { + mAdded.add(fragment); + fragment.mAdded = true; + fragment.mRemoving = false; + if (fragment.mHasMenu && fragment.mMenuVisible) { + mNeedMenuInvalidate = true; + } + if (moveToStateNow) { + moveToState(fragment); + } + } + } + + public void removeFragment(Fragment fragment, int transition, int transitionStyle) { + if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); + final boolean inactive = !fragment.isInBackStack(); + if (!fragment.mDetached || inactive) { + mAdded.remove(fragment); + if (fragment.mHasMenu && fragment.mMenuVisible) { + mNeedMenuInvalidate = true; + } + fragment.mAdded = false; + fragment.mRemoving = true; + moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, + transition, transitionStyle); + } + } + + public void hideFragment(Fragment fragment, int transition, int transitionStyle) { + if (DEBUG) Log.v(TAG, "hide: " + fragment); + if (!fragment.mHidden) { + fragment.mHidden = true; + if (fragment.mView != null) { + Animation anim = loadAnimation(fragment, transition, true, + transitionStyle); + if (anim != null) { + fragment.mView.startAnimation(anim); + } + fragment.mView.setVisibility(View.GONE); + } + if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { + mNeedMenuInvalidate = true; + } + fragment.onHiddenChanged(true); + } + } + + public void showFragment(Fragment fragment, int transition, int transitionStyle) { + if (DEBUG) Log.v(TAG, "show: " + fragment); + if (fragment.mHidden) { + fragment.mHidden = false; + if (fragment.mView != null) { + Animation anim = loadAnimation(fragment, transition, true, + transitionStyle); + if (anim != null) { + fragment.mView.startAnimation(anim); + } + fragment.mView.setVisibility(View.VISIBLE); + } + if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { + mNeedMenuInvalidate = true; + } + fragment.onHiddenChanged(false); + } + } + + public void detachFragment(Fragment fragment, int transition, int transitionStyle) { + if (DEBUG) Log.v(TAG, "detach: " + fragment); + if (!fragment.mDetached) { + fragment.mDetached = true; + if (fragment.mAdded) { + // We are not already in back stack, so need to remove the fragment. + mAdded.remove(fragment); + if (fragment.mHasMenu && fragment.mMenuVisible) { + mNeedMenuInvalidate = true; + } + fragment.mAdded = false; + moveToState(fragment, Fragment.CREATED, transition, transitionStyle); + } + } + } + + public void attachFragment(Fragment fragment, int transition, int transitionStyle) { + if (DEBUG) Log.v(TAG, "attach: " + fragment); + if (fragment.mDetached) { + fragment.mDetached = false; + if (!fragment.mAdded) { + mAdded.add(fragment); + fragment.mAdded = true; + if (fragment.mHasMenu && fragment.mMenuVisible) { + mNeedMenuInvalidate = true; + } + moveToState(fragment, mCurState, transition, transitionStyle); + } + } + } + + public Fragment findFragmentById(int id) { + if (!HONEYCOMB && (id == android.R.id.content)) { + // android.R.id.content would point to the entire content area, + // including the custom action bar + id = R.id.abs__content; + } + + if (mActive != null) { + // First look through added fragments. + for (int i=mAdded.size()-1; i>=0; i--) { + Fragment f = mAdded.get(i); + if (f != null && f.mFragmentId == id) { + return f; + } + } + // Now for any known fragment. + for (int i=mActive.size()-1; i>=0; i--) { + Fragment f = mActive.get(i); + if (f != null && f.mFragmentId == id) { + return f; + } + } + } + return null; + } + + public Fragment findFragmentByTag(String tag) { + if (mActive != null && tag != null) { + // First look through added fragments. + for (int i=mAdded.size()-1; i>=0; i--) { + Fragment f = mAdded.get(i); + if (f != null && tag.equals(f.mTag)) { + return f; + } + } + // Now for any known fragment. + for (int i=mActive.size()-1; i>=0; i--) { + Fragment f = mActive.get(i); + if (f != null && tag.equals(f.mTag)) { + return f; + } + } + } + return null; + } + + public Fragment findFragmentByWho(String who) { + if (mActive != null && who != null) { + for (int i=mActive.size()-1; i>=0; i--) { + Fragment f = mActive.get(i); + if (f != null && who.equals(f.mWho)) { + return f; + } + } + } + return null; + } + + private void checkStateLoss() { + if (mStateSaved) { + throw new IllegalStateException( + "Can not perform this action after onSaveInstanceState"); + } + if (mNoTransactionsBecause != null) { + throw new IllegalStateException( + "Can not perform this action inside of " + mNoTransactionsBecause); + } + } + + public void enqueueAction(Runnable action, boolean allowStateLoss) { + if (!allowStateLoss) { + checkStateLoss(); + } + synchronized (this) { + if (mActivity == null) { + throw new IllegalStateException("Activity has been destroyed"); + } + mActivity.getInternalCallbacks().ensureSupportActionBarAttached(); + if (mPendingActions == null) { + mPendingActions = new ArrayList(); + } + mPendingActions.add(action); + if (mPendingActions.size() == 1) { + mActivity.getInternalCallbacks().getHandler().removeCallbacks(mExecCommit); + mActivity.getInternalCallbacks().getHandler().post(mExecCommit); + } + } + } + + public int allocBackStackIndex(BackStackRecord bse) { + synchronized (this) { + if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) { + if (mBackStackIndices == null) { + mBackStackIndices = new ArrayList(); + } + int index = mBackStackIndices.size(); + if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); + mBackStackIndices.add(bse); + return index; + + } else { + int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); + if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); + mBackStackIndices.set(index, bse); + return index; + } + } + } + + public void setBackStackIndex(int index, BackStackRecord bse) { + synchronized (this) { + if (mBackStackIndices == null) { + mBackStackIndices = new ArrayList(); + } + int N = mBackStackIndices.size(); + if (index < N) { + if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); + mBackStackIndices.set(index, bse); + } else { + while (N < index) { + mBackStackIndices.add(null); + if (mAvailBackStackIndices == null) { + mAvailBackStackIndices = new ArrayList(); + } + if (DEBUG) Log.v(TAG, "Adding available back stack index " + N); + mAvailBackStackIndices.add(N); + N++; + } + if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); + mBackStackIndices.add(bse); + } + } + } + + public void freeBackStackIndex(int index) { + synchronized (this) { + mBackStackIndices.set(index, null); + if (mAvailBackStackIndices == null) { + mAvailBackStackIndices = new ArrayList(); + } + if (DEBUG) Log.v(TAG, "Freeing back stack index " + index); + mAvailBackStackIndices.add(index); + } + } + + /** + * Only call from main thread! + */ + public boolean execPendingActions() { + if (mExecutingActions) { + throw new IllegalStateException("Recursive entry to executePendingTransactions"); + } + + if (Looper.myLooper() != mActivity.getInternalCallbacks().getHandler().getLooper()) { + throw new IllegalStateException("Must be called from main thread of process"); + } + + boolean didSomething = false; + + while (true) { + int numActions; + + synchronized (this) { + if (mPendingActions == null || mPendingActions.size() == 0) { + return didSomething; + } + + numActions = mPendingActions.size(); + if (mTmpActions == null || mTmpActions.length < numActions) { + mTmpActions = new Runnable[numActions]; + } + mPendingActions.toArray(mTmpActions); + mPendingActions.clear(); + mActivity.getInternalCallbacks().getHandler().removeCallbacks(mExecCommit); + } + + mExecutingActions = true; + for (int i=0; i(); + } + mBackStack.add(state); + reportBackStackChanged(); + } + + boolean popBackStackState(Handler handler, String name, int id, int flags) { + if (mBackStack == null) { + return false; + } + if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) { + int last = mBackStack.size()-1; + if (last < 0) { + return false; + } + final BackStackRecord bss = mBackStack.remove(last); + bss.popFromBackStack(true); + reportBackStackChanged(); + } else { + int index = -1; + if (name != null || id >= 0) { + // If a name or ID is specified, look for that place in + // the stack. + index = mBackStack.size()-1; + while (index >= 0) { + BackStackRecord bss = mBackStack.get(index); + if (name != null && name.equals(bss.getName())) { + break; + } + if (id >= 0 && id == bss.mIndex) { + break; + } + index--; + } + if (index < 0) { + return false; + } + if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) { + index--; + // Consume all following entries that match. + while (index >= 0) { + BackStackRecord bss = mBackStack.get(index); + if ((name != null && name.equals(bss.getName())) + || (id >= 0 && id == bss.mIndex)) { + index--; + continue; + } + break; + } + } + } + if (index == mBackStack.size()-1) { + return false; + } + final ArrayList states + = new ArrayList(); + for (int i=mBackStack.size()-1; i>index; i--) { + states.add(mBackStack.remove(i)); + } + final int LAST = states.size()-1; + for (int i=0; i<=LAST; i++) { + if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i)); + states.get(i).popFromBackStack(i == LAST); + } + reportBackStackChanged(); + } + return true; + } + + ArrayList retainNonConfig() { + ArrayList fragments = null; + if (mActive != null) { + for (int i=0; i(); + } + fragments.add(f); + f.mRetaining = true; + f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1; + } + } + } + return fragments; + } + + void saveFragmentViewState(Fragment f) { + if (f.mInnerView == null) { + return; + } + if (mStateArray == null) { + mStateArray = new SparseArray(); + } else { + mStateArray.clear(); + } + f.mInnerView.saveHierarchyState(mStateArray); + if (mStateArray.size() > 0) { + f.mSavedViewState = mStateArray; + mStateArray = null; + } + } + + Bundle saveFragmentBasicState(Fragment f) { + Bundle result = null; + + if (mStateBundle == null) { + mStateBundle = new Bundle(); + } + f.onSaveInstanceState(mStateBundle); + if (!mStateBundle.isEmpty()) { + result = mStateBundle; + mStateBundle = null; + } + + if (f.mView != null) { + saveFragmentViewState(f); + } + if (f.mSavedViewState != null) { + if (result == null) { + result = new Bundle(); + } + result.putSparseParcelableArray( + FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState); + } + + return result; + } + + Parcelable saveAllState() { + // Make sure all pending operations have now been executed to get + // our state update-to-date. + execPendingActions(); + + if (HONEYCOMB) { + // As of Honeycomb, we save state after pausing. Prior to that + // it is before pausing. With fragments this is an issue, since + // there are many things you may do after pausing but before + // stopping that change the fragment state. For those older + // devices, we will not at this point say that we have saved + // the state, so we will allow them to continue doing fragment + // transactions. This retains the same semantics as Honeycomb, + // though you do have the risk of losing the very most recent state + // if the process is killed... we'll live with that. + mStateSaved = true; + } + + if (mActive == null || mActive.size() <= 0) { + return null; + } + + // First collect all active fragments. + int N = mActive.size(); + FragmentState[] active = new FragmentState[N]; + boolean haveFragments = false; + for (int i=0; i Fragment.INITIALIZING && fs.mSavedFragmentState == null) { + fs.mSavedFragmentState = saveFragmentBasicState(f); + + if (f.mTarget != null) { + if (f.mTarget.mIndex < 0) { + String msg = "Failure saving state: " + f + + " has target not in fragment manager: " + f.mTarget; + Log.e(TAG, msg); + dump(" ", null, new PrintWriter(new LogWriter(TAG)), new String[] { }); + throw new IllegalStateException(msg); + } + if (fs.mSavedFragmentState == null) { + fs.mSavedFragmentState = new Bundle(); + } + putFragment(fs.mSavedFragmentState, + FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget); + if (f.mTargetRequestCode != 0) { + fs.mSavedFragmentState.putInt( + FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, + f.mTargetRequestCode); + } + } + + } else { + fs.mSavedFragmentState = f.mSavedFragmentState; + } + + if (DEBUG) Log.v(TAG, "Saved state of " + f + ": " + + fs.mSavedFragmentState); + } + } + + if (!haveFragments) { + if (DEBUG) Log.v(TAG, "saveAllState: no fragments!"); + return null; + } + + int[] added = null; + BackStackState[] backStack = null; + + // Build list of currently added fragments. + if (mAdded != null) { + N = mAdded.size(); + if (N > 0) { + added = new int[N]; + for (int i=0; i 0) { + backStack = new BackStackState[N]; + for (int i=0; i nonConfig) { + // If there is no saved state at all, then there can not be + // any nonConfig fragments either, so that is that. + if (state == null) return; + FragmentManagerState fms = (FragmentManagerState)state; + if (fms.mActive == null) return; + + // First re-attach any non-config instances we are retaining back + // to their saved state, so we don't try to instantiate them again. + if (nonConfig != null) { + for (int i=0; i(fms.mActive.length); + if (mAvailIndices != null) { + mAvailIndices.clear(); + } + for (int i=0; i(); + } + if (DEBUG) Log.v(TAG, "restoreAllState: adding avail #" + i); + mAvailIndices.add(i); + } + } + + // Update the target of all retained fragments. + if (nonConfig != null) { + for (int i=0; i= 0) { + if (f.mTargetIndex < mActive.size()) { + f.mTarget = mActive.get(f.mTargetIndex); + } else { + Log.w(TAG, "Re-attaching retained fragment " + f + + " target no longer exists: " + f.mTargetIndex); + f.mTarget = null; + } + } + } + } + + // Build the list of currently added fragments. + if (fms.mAdded != null) { + mAdded = new ArrayList(fms.mAdded.length); + for (int i=0; i(fms.mBackStack.length); + for (int i=0; i= 0) { + setBackStackIndex(bse.mIndex, bse); + } + } + } else { + mBackStack = null; + } + } + + public void attachActivity(SupportActivity activity) { + if (mActivity != null) throw new IllegalStateException(); + mActivity = activity; + } + + public void noteStateNotSaved() { + mStateSaved = false; + } + + public void dispatchCreate() { + mStateSaved = false; + moveToState(Fragment.CREATED, false); + } + + public void dispatchActivityCreated() { + mStateSaved = false; + moveToState(Fragment.ACTIVITY_CREATED, false); + } + + public void dispatchStart() { + mStateSaved = false; + moveToState(Fragment.STARTED, false); + } + + public void dispatchResume() { + mStateSaved = false; + moveToState(Fragment.RESUMED, false); + } + + public void dispatchPause() { + moveToState(Fragment.STARTED, false); + } + + public void dispatchStop() { + // See saveAllState() for the explanation of this. We do this for + // all platform versions, to keep our behavior more consistent between + // them. + mStateSaved = true; + + moveToState(Fragment.STOPPED, false); + } + + public void dispatchReallyStop() { + moveToState(Fragment.ACTIVITY_CREATED, false); + } + + public void dispatchDestroy() { + mDestroyed = true; + execPendingActions(); + moveToState(Fragment.INITIALIZING, false); + mActivity = null; + } + + public void dispatchConfigurationChanged(Configuration newConfig) { + if (mActive != null) { + for (int i=0; i newMenus = null; + if (mActive != null) { + for (int i=0; i(); + } + newMenus.add(f); + } + } + } + + if (mCreatedMenus != null) { + for (int i=0; i mSavedState = new ArrayList(); + private ArrayList mFragments = new ArrayList(); + private Fragment mCurrentPrimaryItem = null; + + public FragmentStatePagerAdapter(FragmentManager fm) { + mFragmentManager = fm; + } + + /** + * Return the Fragment associated with a specified position. + */ + public abstract Fragment getItem(int position); + + @Override + public void startUpdate(View container) { + } + + @Override + public Object instantiateItem(View container, int position) { + // If we already have this item instantiated, there is nothing + // to do. This can happen when we are restoring the entire pager + // from its saved state, where the fragment manager has already + // taken care of restoring the fragments we previously had instantiated. + if (mFragments.size() > position) { + Fragment f = mFragments.get(position); + if (f != null) { + return f; + } + } + + if (mCurTransaction == null) { + mCurTransaction = mFragmentManager.beginTransaction(); + } + + Fragment fragment = getItem(position); + if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment); + if (mSavedState.size() > position) { + Fragment.SavedState fss = mSavedState.get(position); + if (fss != null) { + fragment.setInitialSavedState(fss); + } + } + while (mFragments.size() <= position) { + mFragments.add(null); + } + fragment.setMenuVisibility(false); + mFragments.set(position, fragment); + mCurTransaction.add(container.getId(), fragment); + + return fragment; + } + + @Override + public void destroyItem(View container, int position, Object object) { + Fragment fragment = (Fragment)object; + + if (mCurTransaction == null) { + mCurTransaction = mFragmentManager.beginTransaction(); + } + if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object + + " v=" + ((Fragment)object).getView()); + while (mSavedState.size() <= position) { + mSavedState.add(null); + } + mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment)); + mFragments.set(position, null); + + mCurTransaction.remove(fragment); + } + + @Override + public void setPrimaryItem(View container, int position, Object object) { + Fragment fragment = (Fragment)object; + if (fragment != mCurrentPrimaryItem) { + if (mCurrentPrimaryItem != null) { + mCurrentPrimaryItem.setMenuVisibility(false); + } + if (fragment != null) { + fragment.setMenuVisibility(true); + } + mCurrentPrimaryItem = fragment; + } + } + + @Override + public void finishUpdate(View container) { + if (mCurTransaction != null) { + mCurTransaction.commitAllowingStateLoss(); + mCurTransaction = null; + mFragmentManager.executePendingTransactions(); + } + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return ((Fragment)object).getView() == view; + } + + @Override + public Parcelable saveState() { + Bundle state = null; + if (mSavedState.size() > 0) { + state = new Bundle(); + Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()]; + mSavedState.toArray(fss); + state.putParcelableArray("states", fss); + } + for (int i=0; i keys = bundle.keySet(); + for (String key: keys) { + if (key.startsWith("f")) { + int index = Integer.parseInt(key.substring(1)); + Fragment f = mFragmentManager.getFragment(bundle, key); + if (f != null) { + while (mFragments.size() <= index) { + mFragments.add(null); + } + f.setMenuVisibility(false); + mFragments.set(index, f); + } else { + Log.w(TAG, "Bad fragment at key " + key); + } + } + } + } + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/app/FragmentTransaction.java b/actionbarsherlock/library/src/android/support/v4/app/FragmentTransaction.java new file mode 100644 index 000000000..252873ae1 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/FragmentTransaction.java @@ -0,0 +1,272 @@ +/* + * 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 android.support.v4.app; + +/** + * Static library support version of the framework's {@link android.app.FragmentTransaction}. + * Used to write apps that run on platforms prior to Android 3.0. When running + * on Android 3.0 or above, this implementation is still used; it does not try + * to switch to the framework's implementation. See the framework SDK + * documentation for a class overview. + */ +public abstract class FragmentTransaction { + /** + * Calls {@link #add(int, Fragment, String)} with a 0 containerViewId. + */ + public abstract FragmentTransaction add(Fragment fragment, String tag); + + /** + * Calls {@link #add(int, Fragment, String)} with a null tag. + */ + public abstract FragmentTransaction add(int containerViewId, Fragment fragment); + + /** + * Add a fragment to the activity state. This fragment may optionally + * also have its view (if {@link Fragment#onCreateView Fragment.onCreateView} + * returns non-null) into a container view of the activity. + * + * @param containerViewId Optional identifier of the container this fragment is + * to be placed in. If 0, it will not be placed in a container. + * @param fragment The fragment to be added. This fragment must not already + * be added to the activity. + * @param tag Optional tag name for the fragment, to later retrieve the + * fragment with {@link FragmentManager#findFragmentByTag(String) + * FragmentManager.findFragmentByTag(String)}. + * + * @return Returns the same FragmentTransaction instance. + */ + public abstract FragmentTransaction add(int containerViewId, Fragment fragment, String tag); + + /** + * Calls {@link #replace(int, Fragment, String)} with a null tag. + */ + public abstract FragmentTransaction replace(int containerViewId, Fragment fragment); + + /** + * Replace an existing fragment that was added to a container. This is + * essentially the same as calling {@link #remove(Fragment)} for all + * currently added fragments that were added with the same containerViewId + * and then {@link #add(int, Fragment, String)} with the same arguments + * given here. + * + * @param containerViewId Identifier of the container whose fragment(s) are + * to be replaced. + * @param fragment The new fragment to place in the container. + * @param tag Optional tag name for the fragment, to later retrieve the + * fragment with {@link FragmentManager#findFragmentByTag(String) + * FragmentManager.findFragmentByTag(String)}. + * + * @return Returns the same FragmentTransaction instance. + */ + public abstract FragmentTransaction replace(int containerViewId, Fragment fragment, String tag); + + /** + * Remove an existing fragment. If it was added to a container, its view + * is also removed from that container. + * + * @param fragment The fragment to be removed. + * + * @return Returns the same FragmentTransaction instance. + */ + public abstract FragmentTransaction remove(Fragment fragment); + + /** + * Hides an existing fragment. This is only relevant for fragments whose + * views have been added to a container, as this will cause the view to + * be hidden. + * + * @param fragment The fragment to be hidden. + * + * @return Returns the same FragmentTransaction instance. + */ + public abstract FragmentTransaction hide(Fragment fragment); + + /** + * Shows a previously hidden fragment. This is only relevant for fragments whose + * views have been added to a container, as this will cause the view to + * be shown. + * + * @param fragment The fragment to be shown. + * + * @return Returns the same FragmentTransaction instance. + */ + public abstract FragmentTransaction show(Fragment fragment); + + /** + * Detach the given fragment from the UI. This is the same state as + * when it is put on the back stack: the fragment is removed from + * the UI, however its state is still being actively managed by the + * fragment manager. When going into this state its view hierarchy + * is destroyed. + * + * @param fragment The fragment to be detached. + * + * @return Returns the same FragmentTransaction instance. + */ + public abstract FragmentTransaction detach(Fragment fragment); + + /** + * Re-attach a fragment after it had previously been deatched from + * the UI with {@link #detach(Fragment)}. This + * causes its view hierarchy to be re-created, attached to the UI, + * and displayed. + * + * @param fragment The fragment to be attached. + * + * @return Returns the same FragmentTransaction instance. + */ + public abstract FragmentTransaction attach(Fragment fragment); + + /** + * @return true if this transaction contains no operations, + * false otherwise. + */ + public abstract boolean isEmpty(); + + /** + * Bit mask that is set for all enter transitions. + */ + public static final int TRANSIT_ENTER_MASK = 0x1000; + + /** + * Bit mask that is set for all exit transitions. + */ + public static final int TRANSIT_EXIT_MASK = 0x2000; + + /** Not set up for a transition. */ + public static final int TRANSIT_UNSET = -1; + /** No animation for transition. */ + public static final int TRANSIT_NONE = 0; + /** Fragment is being added onto the stack */ + public static final int TRANSIT_FRAGMENT_OPEN = 1 | TRANSIT_ENTER_MASK; + /** Fragment is being removed from the stack */ + public static final int TRANSIT_FRAGMENT_CLOSE = 2 | TRANSIT_EXIT_MASK; + /** Fragment should simply fade in or out; that is, no strong navigation associated + * with it except that it is appearing or disappearing for some reason. */ + public static final int TRANSIT_FRAGMENT_FADE = 3 | TRANSIT_ENTER_MASK; + + /** + * Set specific animation resources to run for the fragments that are + * entering and exiting in this transaction. These animations will not be + * played when popping the back stack. + */ + public abstract FragmentTransaction setCustomAnimations(int enter, int exit); + + /** + * Set specific animation resources to run for the fragments that are + * entering and exiting in this transaction. The popEnter + * and popExit animations will be played for enter/exit + * operations specifically when popping the back stack. + */ + public abstract FragmentTransaction setCustomAnimations(int enter, int exit, + int popEnter, int popExit); + + /** + * Select a standard transition animation for this transaction. May be + * one of {@link #TRANSIT_NONE}, {@link #TRANSIT_FRAGMENT_OPEN}, + * or {@link #TRANSIT_FRAGMENT_CLOSE} + */ + public abstract FragmentTransaction setTransition(int transit); + + /** + * Set a custom style resource that will be used for resolving transit + * animations. + */ + public abstract FragmentTransaction setTransitionStyle(int styleRes); + + /** + * Add this transaction to the back stack. This means that the transaction + * will be remembered after it is committed, and will reverse its operation + * when later popped off the stack. + * + * @param name An optional name for this back stack state, or null. + */ + public abstract FragmentTransaction addToBackStack(String name); + + /** + * Returns true if this FragmentTransaction is allowed to be added to the back + * stack. If this method would return false, {@link #addToBackStack(String)} + * will throw {@link IllegalStateException}. + * + * @return True if {@link #addToBackStack(String)} is permitted on this transaction. + */ + public abstract boolean isAddToBackStackAllowed(); + + /** + * Disallow calls to {@link #addToBackStack(String)}. Any future calls to + * addToBackStack will throw {@link IllegalStateException}. If addToBackStack + * has already been called, this method will throw IllegalStateException. + */ + public abstract FragmentTransaction disallowAddToBackStack(); + + /** + * Set the full title to show as a bread crumb when this transaction + * is on the back stack, as used by {@link FragmentBreadCrumbs}. + * + * @param res A string resource containing the title. + */ + public abstract FragmentTransaction setBreadCrumbTitle(int res); + + /** + * Like {@link #setBreadCrumbTitle(int)} but taking a raw string; this + * method is not recommended, as the string can not be changed + * later if the locale changes. + */ + public abstract FragmentTransaction setBreadCrumbTitle(CharSequence text); + + /** + * Set the short title to show as a bread crumb when this transaction + * is on the back stack, as used by {@link FragmentBreadCrumbs}. + * + * @param res A string resource containing the title. + */ + public abstract FragmentTransaction setBreadCrumbShortTitle(int res); + + /** + * Like {@link #setBreadCrumbShortTitle(int)} but taking a raw string; this + * method is not recommended, as the string can not be changed + * later if the locale changes. + */ + public abstract FragmentTransaction setBreadCrumbShortTitle(CharSequence text); + + /** + * Schedules a commit of this transaction. The commit does + * not happen immediately; it will be scheduled as work on the main thread + * to be done the next time that thread is ready. + * + *

A transaction can only be committed with this method + * prior to its containing activity saving its state. If the commit is + * attempted after that point, an exception will be thrown. This is + * because the state after the commit can be lost if the activity needs to + * be restored from its state. See {@link #commitAllowingStateLoss()} for + * situations where it may be okay to lose the commit.

+ * + * @return Returns the identifier of this transaction's back stack entry, + * if {@link #addToBackStack(String)} had been called. Otherwise, returns + * a negative number. + */ + public abstract int commit(); + + /** + * Like {@link #commit} but allows the commit to be executed after an + * activity's state is saved. This is dangerous because the commit can + * be lost if the activity needs to later be restored from its state, so + * this should only be used for cases where it is okay for the UI state + * to change unexpectedly on the user. + */ + public abstract int commitAllowingStateLoss(); +} diff --git a/actionbarsherlock/library/src/android/support/v4/app/HCSparseArray.java b/actionbarsherlock/library/src/android/support/v4/app/HCSparseArray.java new file mode 100644 index 000000000..f6cf9fe26 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/HCSparseArray.java @@ -0,0 +1,360 @@ +/* + * 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 android.support.v4.app; + +/** + * A copy of Honeycomb's SparseArray, only so we can have the removeAt() method. + */ +public class HCSparseArray { + private static final Object DELETED = new Object(); + private boolean mGarbage = false; + + /** + * Creates a new SparseArray containing no mappings. + */ + public HCSparseArray() { + this(10); + } + + /** + * Creates a new SparseArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. + */ + public HCSparseArray(int initialCapacity) { + initialCapacity = idealIntArraySize(initialCapacity); + + mKeys = new int[initialCapacity]; + mValues = new Object[initialCapacity]; + mSize = 0; + } + + /** + * Gets the Object mapped from the specified key, or null + * if no such mapping has been made. + */ + public E get(int key) { + return get(key, null); + } + + /** + * Gets the Object mapped from the specified key, or the specified Object + * if no such mapping has been made. + */ + @SuppressWarnings("unchecked") + public E get(int key, E valueIfKeyNotFound) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i < 0 || mValues[i] == DELETED) { + return valueIfKeyNotFound; + } else { + return (E) mValues[i]; + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(int key) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + if (mValues[i] != DELETED) { + mValues[i] = DELETED; + mGarbage = true; + } + } + } + + /** + * Alias for {@link #delete(int)}. + */ + public void remove(int key) { + delete(key); + } + + /** + * Removes the mapping at the specified index. + */ + public void removeAt(int index) { + if (mValues[index] != DELETED) { + mValues[index] = DELETED; + mGarbage = true; + } + } + + private void gc() { + // Log.e("SparseArray", "gc start with " + mSize); + + int n = mSize; + int o = 0; + int[] keys = mKeys; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + Object val = values[i]; + + if (val != DELETED) { + if (i != o) { + keys[o] = keys[i]; + values[o] = val; + } + + o++; + } + } + + mGarbage = false; + mSize = o; + + // Log.e("SparseArray", "gc end with " + mSize); + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(int key, E value) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + mValues[i] = value; + } else { + i = ~i; + + if (i < mSize && mValues[i] == DELETED) { + mKeys[i] = key; + mValues[i] = value; + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + + // Search again because indices may have changed. + i = ~binarySearch(mKeys, 0, mSize, key); + } + + if (mSize >= mKeys.length) { + int n = idealIntArraySize(mSize + 1); + + int[] nkeys = new int[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + if (mSize - i != 0) { + // Log.e("SparseArray", "move " + (mSize - i)); + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = value; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseArray + * currently stores. + */ + public int size() { + if (mGarbage) { + gc(); + } + + return mSize; + } + + /** + * Given an index in the range 0...size()-1, returns + * the key from the indexth key-value mapping that this + * SparseArray stores. + */ + public int keyAt(int index) { + if (mGarbage) { + gc(); + } + + return mKeys[index]; + } + + /** + * Given an index in the range 0...size()-1, returns + * the value from the indexth key-value mapping that this + * SparseArray stores. + */ + @SuppressWarnings("unchecked") + public E valueAt(int index) { + if (mGarbage) { + gc(); + } + + return (E) mValues[index]; + } + + /** + * Given an index in the range 0...size()-1, sets a new + * value for the indexth key-value mapping that this + * SparseArray stores. + */ + public void setValueAt(int index, E value) { + if (mGarbage) { + gc(); + } + + mValues[index] = value; + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(int key) { + if (mGarbage) { + gc(); + } + + return binarySearch(mKeys, 0, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(E value) { + if (mGarbage) { + gc(); + } + + for (int i = 0; i < mSize; i++) + if (mValues[i] == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseArray. + */ + public void clear() { + int n = mSize; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + values[i] = null; + } + + mSize = 0; + mGarbage = false; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(int key, E value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + } + + int pos = mSize; + if (pos >= mKeys.length) { + int n = idealIntArraySize(pos + 1); + + int[] nkeys = new int[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + mKeys[pos] = key; + mValues[pos] = value; + mSize = pos + 1; + } + + private static int binarySearch(int[] a, int start, int len, int key) { + int high = start + len, low = start - 1, guess; + + while (high - low > 1) { + guess = (high + low) / 2; + + if (a[guess] < key) + low = guess; + else + high = guess; + } + + if (high == start + len) + return ~(start + len); + else if (a[high] == key) + return high; + else + return ~high; + } + + /*private void checkIntegrity() { + for (int i = 1; i < mSize; i++) { + if (mKeys[i] <= mKeys[i - 1]) { + for (int j = 0; j < mSize; j++) { + Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]); + } + + throw new RuntimeException(); + } + } + }*/ + + static int idealByteArraySize(int need) { + for (int i = 4; i < 32; i++) + if (need <= (1 << i) - 12) + return (1 << i) - 12; + + return need; + } + + static int idealIntArraySize(int need) { + return idealByteArraySize(need * 4) / 4; + } + + private int[] mKeys; + private Object[] mValues; + private int mSize; +} diff --git a/actionbarsherlock/library/src/android/support/v4/app/ListFragment.java b/actionbarsherlock/library/src/android/support/v4/app/ListFragment.java new file mode 100644 index 000000000..7f8666fff --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/ListFragment.java @@ -0,0 +1,375 @@ +/* + * 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 android.support.v4.app; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AnimationUtils; +import android.widget.AdapterView; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.TextView; + +/** + * Static library support version of the framework's {@link android.app.ListFragment}. + * Used to write apps that run on platforms prior to Android 3.0. When running + * on Android 3.0 or above, this implementation is still used; it does not try + * to switch to the framework's implementation. See the framework SDK + * documentation for a class overview. + */ +public class ListFragment extends Fragment { + static final int INTERNAL_EMPTY_ID = 0x00ff0001; + static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002; + static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003; + + final private Handler mHandler = new Handler(); + + final private Runnable mRequestFocus = new Runnable() { + public void run() { + mList.focusableViewAvailable(mList); + } + }; + + final private AdapterView.OnItemClickListener mOnClickListener + = new AdapterView.OnItemClickListener() { + public void onItemClick(AdapterView parent, View v, int position, long id) { + onListItemClick((ListView)parent, v, position, id); + } + }; + + ListAdapter mAdapter; + ListView mList; + View mEmptyView; + TextView mStandardEmptyView; + View mProgressContainer; + View mListContainer; + CharSequence mEmptyText; + boolean mListShown; + + public ListFragment() { + } + + /** + * Provide default implementation to return a simple list view. Subclasses + * can override to replace with their own layout. If doing so, the + * returned view hierarchy must have a ListView whose id + * is {@link android.R.id#list android.R.id.list} and can optionally + * have a sibling view id {@link android.R.id#empty android.R.id.empty} + * that is to be shown when the list is empty. + * + *

If you are overriding this method with your own custom content, + * consider including the standard layout {@link android.R.layout#list_content} + * in your layout file, so that you continue to retain all of the standard + * behavior of ListFragment. In particular, this is currently the only + * way to have the built-in indeterminant progress state be shown. + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final Context context = getActivity(); + + FrameLayout root = new FrameLayout(context); + + // ------------------------------------------------------------------ + + LinearLayout pframe = new LinearLayout(context); + pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID); + pframe.setOrientation(LinearLayout.VERTICAL); + pframe.setVisibility(View.GONE); + pframe.setGravity(Gravity.CENTER); + + ProgressBar progress = new ProgressBar(context, null, + android.R.attr.progressBarStyleLarge); + pframe.addView(progress, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + root.addView(pframe, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + + // ------------------------------------------------------------------ + + FrameLayout lframe = new FrameLayout(context); + lframe.setId(INTERNAL_LIST_CONTAINER_ID); + + TextView tv = new TextView(context); + tv.setId(INTERNAL_EMPTY_ID); + tv.setGravity(Gravity.CENTER); + lframe.addView(tv, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + + ListView lv = new ListView(context); + lv.setId(android.R.id.list); + lv.setDrawSelectorOnTop(false); + lframe.addView(lv, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + + root.addView(lframe, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + + // ------------------------------------------------------------------ + + root.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + + return root; + } + + /** + * Attach to list view once the view hierarchy has been created. + */ + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + ensureList(); + } + + /** + * Detach from list view. + */ + @Override + public void onDestroyView() { + mHandler.removeCallbacks(mRequestFocus); + mList = null; + mListShown = false; + mEmptyView = mProgressContainer = mListContainer = null; + mStandardEmptyView = null; + super.onDestroyView(); + } + + /** + * This method will be called when an item in the list is selected. + * Subclasses should override. Subclasses can call + * getListView().getItemAtPosition(position) if they need to access the + * data associated with the selected item. + * + * @param l The ListView where the click happened + * @param v The view that was clicked within the ListView + * @param position The position of the view in the list + * @param id The row id of the item that was clicked + */ + public void onListItemClick(ListView l, View v, int position, long id) { + } + + /** + * Provide the cursor for the list view. + */ + public void setListAdapter(ListAdapter adapter) { + boolean hadAdapter = mAdapter != null; + mAdapter = adapter; + if (mList != null) { + mList.setAdapter(adapter); + if (!mListShown && !hadAdapter) { + // The list was hidden, and previously didn't have an + // adapter. It is now time to show it. + setListShown(true, getView().getWindowToken() != null); + } + } + } + + /** + * Set the currently selected list item to the specified + * position with the adapter's data + * + * @param position + */ + public void setSelection(int position) { + ensureList(); + mList.setSelection(position); + } + + /** + * Get the position of the currently selected list item. + */ + public int getSelectedItemPosition() { + ensureList(); + return mList.getSelectedItemPosition(); + } + + /** + * Get the cursor row ID of the currently selected list item. + */ + public long getSelectedItemId() { + ensureList(); + return mList.getSelectedItemId(); + } + + /** + * Get the activity's list view widget. + */ + public ListView getListView() { + ensureList(); + return mList; + } + + /** + * The default content for a ListFragment has a TextView that can + * be shown when the list is empty. If you would like to have it + * shown, call this method to supply the text it should use. + */ + public void setEmptyText(CharSequence text) { + ensureList(); + if (mStandardEmptyView == null) { + throw new IllegalStateException("Can't be used with a custom content view"); + } + mStandardEmptyView.setText(text); + if (mEmptyText == null) { + mList.setEmptyView(mStandardEmptyView); + } + mEmptyText = text; + } + + /** + * Control whether the list is being displayed. You can make it not + * displayed if you are waiting for the initial data to show in it. During + * this time an indeterminant progress indicator will be shown instead. + * + *

Applications do not normally need to use this themselves. The default + * behavior of ListFragment is to start with the list not being shown, only + * showing it once an adapter is given with {@link #setListAdapter(ListAdapter)}. + * If the list at that point had not been shown, when it does get shown + * it will be do without the user ever seeing the hidden state. + * + * @param shown If true, the list view is shown; if false, the progress + * indicator. The initial value is true. + */ + public void setListShown(boolean shown) { + setListShown(shown, true); + } + + /** + * Like {@link #setListShown(boolean)}, but no animation is used when + * transitioning from the previous state. + */ + public void setListShownNoAnimation(boolean shown) { + setListShown(shown, false); + } + + /** + * Control whether the list is being displayed. You can make it not + * displayed if you are waiting for the initial data to show in it. During + * this time an indeterminant progress indicator will be shown instead. + * + * @param shown If true, the list view is shown; if false, the progress + * indicator. The initial value is true. + * @param animate If true, an animation will be used to transition to the + * new state. + */ + private void setListShown(boolean shown, boolean animate) { + ensureList(); + if (mProgressContainer == null) { + throw new IllegalStateException("Can't be used with a custom content view"); + } + if (mListShown == shown) { + return; + } + mListShown = shown; + if (shown) { + if (animate) { + mProgressContainer.startAnimation(AnimationUtils.loadAnimation( + getActivity(), android.R.anim.fade_out)); + mListContainer.startAnimation(AnimationUtils.loadAnimation( + getActivity(), android.R.anim.fade_in)); + } else { + mProgressContainer.clearAnimation(); + mListContainer.clearAnimation(); + } + mProgressContainer.setVisibility(View.GONE); + mListContainer.setVisibility(View.VISIBLE); + } else { + if (animate) { + mProgressContainer.startAnimation(AnimationUtils.loadAnimation( + getActivity(), android.R.anim.fade_in)); + mListContainer.startAnimation(AnimationUtils.loadAnimation( + getActivity(), android.R.anim.fade_out)); + } else { + mProgressContainer.clearAnimation(); + mListContainer.clearAnimation(); + } + mProgressContainer.setVisibility(View.VISIBLE); + mListContainer.setVisibility(View.GONE); + } + } + + /** + * Get the ListAdapter associated with this activity's ListView. + */ + public ListAdapter getListAdapter() { + return mAdapter; + } + + private void ensureList() { + if (mList != null) { + return; + } + View root = getView(); + if (root == null) { + throw new IllegalStateException("Content view not yet created"); + } + if (root instanceof ListView) { + mList = (ListView)root; + } else { + mStandardEmptyView = (TextView)root.findViewById(INTERNAL_EMPTY_ID); + if (mStandardEmptyView == null) { + mEmptyView = root.findViewById(android.R.id.empty); + } else { + mStandardEmptyView.setVisibility(View.GONE); + } + mProgressContainer = root.findViewById(INTERNAL_PROGRESS_CONTAINER_ID); + mListContainer = root.findViewById(INTERNAL_LIST_CONTAINER_ID); + View rawListView = root.findViewById(android.R.id.list); + if (!(rawListView instanceof ListView)) { + if (rawListView == null) { + throw new RuntimeException( + "Your content must have a ListView whose id attribute is " + + "'android.R.id.list'"); + } + throw new RuntimeException( + "Content has view with id attribute 'android.R.id.list' " + + "that is not a ListView class"); + } + mList = (ListView)rawListView; + if (mEmptyView != null) { + mList.setEmptyView(mEmptyView); + } else if (mEmptyText != null) { + mStandardEmptyView.setText(mEmptyText); + mList.setEmptyView(mStandardEmptyView); + } + } + mListShown = true; + mList.setOnItemClickListener(mOnClickListener); + if (mAdapter != null) { + ListAdapter adapter = mAdapter; + mAdapter = null; + setListAdapter(adapter); + } else { + // We are starting without an adapter, so assume we won't + // have our data right away and start with the progress indicator. + if (mProgressContainer != null) { + setListShown(false, false); + } + } + mHandler.post(mRequestFocus); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/app/LoaderManager.java b/actionbarsherlock/library/src/android/support/v4/app/LoaderManager.java new file mode 100644 index 000000000..69258ce8c --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/LoaderManager.java @@ -0,0 +1,803 @@ +/* + * 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 android.support.v4.app; + +import android.os.Bundle; +import android.support.v4.content.Loader; +import android.support.v4.util.DebugUtils; +import android.util.Log; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.reflect.Modifier; + +/** + * Static library support version of the framework's {@link android.app.LoaderManager}. + * Used to write apps that run on platforms prior to Android 3.0. When running + * on Android 3.0 or above, this implementation is still used; it does not try + * to switch to the framework's implementation. See the framework SDK + * documentation for a class overview. + * + *

Your activity must derive from {@link FragmentActivity} to use this. + */ +public abstract class LoaderManager { + /** + * Callback interface for a client to interact with the manager. + */ + public interface LoaderCallbacks { + /** + * Instantiate and return a new Loader for the given ID. + * + * @param id The ID whose loader is to be created. + * @param args Any arguments supplied by the caller. + * @return Return a new Loader instance that is ready to start loading. + */ + public Loader onCreateLoader(int id, Bundle args); + + /** + * Called when a previously created loader has finished its load. Note + * that normally an application is not allowed to commit fragment + * transactions while in this call, since it can happen after an + * activity's state is saved. See {@link FragmentManager#beginTransaction() + * FragmentManager.openTransaction()} for further discussion on this. + * + *

This function is guaranteed to be called prior to the release of + * the last data that was supplied for this Loader. At this point + * you should remove all use of the old data (since it will be released + * soon), but should not do your own release of the data since its Loader + * owns it and will take care of that. The Loader will take care of + * management of its data so you don't have to. In particular: + * + *

    + *
  • The Loader will monitor for changes to the data, and report + * them to you through new calls here. You should not monitor the + * data yourself. For example, if the data is a {@link android.database.Cursor} + * and you place it in a {@link android.widget.CursorAdapter}, use + * the {@link android.widget.CursorAdapter#CursorAdapter(android.content.Context, + * android.database.Cursor, int)} constructor without passing + * in either {@link android.widget.CursorAdapter#FLAG_AUTO_REQUERY} + * or {@link android.widget.CursorAdapter#FLAG_REGISTER_CONTENT_OBSERVER} + * (that is, use 0 for the flags argument). This prevents the CursorAdapter + * from doing its own observing of the Cursor, which is not needed since + * when a change happens you will get a new Cursor throw another call + * here. + *

  • The Loader will release the data once it knows the application + * is no longer using it. For example, if the data is + * a {@link android.database.Cursor} from a {@link android.content.CursorLoader}, + * you should not call close() on it yourself. If the Cursor is being placed in a + * {@link android.widget.CursorAdapter}, you should use the + * {@link android.widget.CursorAdapter#swapCursor(android.database.Cursor)} + * method so that the old Cursor is not closed. + *
+ * + * @param loader The Loader that has finished. + * @param data The data generated by the Loader. + */ + public void onLoadFinished(Loader loader, D data); + + /** + * Called when a previously created loader is being reset, and thus + * making its data unavailable. The application should at this point + * remove any references it has to the Loader's data. + * + * @param loader The Loader that is being reset. + */ + public void onLoaderReset(Loader loader); + } + + /** + * Ensures a loader is initialized and active. If the loader doesn't + * already exist, one is created and (if the activity/fragment is currently + * started) starts the loader. Otherwise the last created + * loader is re-used. + * + *

In either case, the given callback is associated with the loader, and + * will be called as the loader state changes. If at the point of call + * the caller is in its started state, and the requested loader + * already exists and has generated its data, then + * callback {@link LoaderCallbacks#onLoadFinished} will + * be called immediately (inside of this function), so you must be prepared + * for this to happen. + * + * @param id A unique identifier for this loader. Can be whatever you want. + * Identifiers are scoped to a particular LoaderManager instance. + * @param args Optional arguments to supply to the loader at construction. + * If a loader already exists (a new one does not need to be created), this + * parameter will be ignored and the last arguments continue to be used. + * @param callback Interface the LoaderManager will call to report about + * changes in the state of the loader. Required. + */ + public abstract Loader initLoader(int id, Bundle args, + LoaderManager.LoaderCallbacks callback); + + /** + * Starts a new or restarts an existing {@link android.content.Loader} in + * this manager, registers the callbacks to it, + * and (if the activity/fragment is currently started) starts loading it. + * If a loader with the same id has previously been + * started it will automatically be destroyed when the new loader completes + * its work. The callback will be delivered before the old loader + * is destroyed. + * + * @param id A unique identifier for this loader. Can be whatever you want. + * Identifiers are scoped to a particular LoaderManager instance. + * @param args Optional arguments to supply to the loader at construction. + * @param callback Interface the LoaderManager will call to report about + * changes in the state of the loader. Required. + */ + public abstract Loader restartLoader(int id, Bundle args, + LoaderManager.LoaderCallbacks callback); + + /** + * Stops and removes the loader with the given ID. If this loader + * had previously reported data to the client through + * {@link LoaderCallbacks#onLoadFinished(Loader, Object)}, a call + * will be made to {@link LoaderCallbacks#onLoaderReset(Loader)}. + */ + public abstract void destroyLoader(int id); + + /** + * Return the Loader with the given id or null if no matching Loader + * is found. + */ + public abstract Loader getLoader(int id); + + /** + * Print the LoaderManager's state into the given stream. + * + * @param prefix Text to print at the front of each line. + * @param fd The raw file descriptor that the dump is being sent to. + * @param writer A PrintWriter to which the dump is to be set. + * @param args Additional arguments to the dump request. + */ + public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); + + /** + * Control whether the framework's internal loader manager debugging + * logs are turned on. If enabled, you will see output in logcat as + * the framework performs loader operations. + */ + public static void enableDebugLogging(boolean enabled) { + LoaderManagerImpl.DEBUG = enabled; + } +} + +class LoaderManagerImpl extends LoaderManager { + static final String TAG = "LoaderManager"; + static boolean DEBUG = false; + + // These are the currently active loaders. A loader is here + // from the time its load is started until it has been explicitly + // stopped or restarted by the application. + final HCSparseArray mLoaders = new HCSparseArray(); + + // These are previously run loaders. This list is maintained internally + // to avoid destroying a loader while an application is still using it. + // It allows an application to restart a loader, but continue using its + // previously run loader until the new loader's data is available. + final HCSparseArray mInactiveLoaders = new HCSparseArray(); + + SupportActivity mActivity; + boolean mStarted; + boolean mRetaining; + boolean mRetainingStarted; + + boolean mCreatingLoader; + + final class LoaderInfo implements Loader.OnLoadCompleteListener { + final int mId; + final Bundle mArgs; + LoaderManager.LoaderCallbacks mCallbacks; + Loader mLoader; + boolean mHaveData; + boolean mDeliveredData; + Object mData; + boolean mStarted; + boolean mRetaining; + boolean mRetainingStarted; + boolean mReportNextStart; + boolean mDestroyed; + boolean mListenerRegistered; + + LoaderInfo mPendingLoader; + + public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks callbacks) { + mId = id; + mArgs = args; + mCallbacks = callbacks; + } + + void start() { + if (mRetaining && mRetainingStarted) { + // Our owner is started, but we were being retained from a + // previous instance in the started state... so there is really + // nothing to do here, since the loaders are still started. + mStarted = true; + return; + } + + if (mStarted) { + // If loader already started, don't restart. + return; + } + + mStarted = true; + + if (DEBUG) Log.v(TAG, " Starting: " + this); + if (mLoader == null && mCallbacks != null) { + mLoader = mCallbacks.onCreateLoader(mId, mArgs); + } + if (mLoader != null) { + if (mLoader.getClass().isMemberClass() + && !Modifier.isStatic(mLoader.getClass().getModifiers())) { + throw new IllegalArgumentException( + "Object returned from onCreateLoader must not be a non-static inner member class: " + + mLoader); + } + if (!mListenerRegistered) { + mLoader.registerListener(mId, this); + mListenerRegistered = true; + } + mLoader.startLoading(); + } + } + + void retain() { + if (DEBUG) Log.v(TAG, " Retaining: " + this); + mRetaining = true; + mRetainingStarted = mStarted; + mStarted = false; + mCallbacks = null; + } + + void finishRetain() { + if (mRetaining) { + if (DEBUG) Log.v(TAG, " Finished Retaining: " + this); + mRetaining = false; + if (mStarted != mRetainingStarted) { + if (!mStarted) { + // This loader was retained in a started state, but + // at the end of retaining everything our owner is + // no longer started... so make it stop. + stop(); + } + } + } + + if (mStarted && mHaveData && !mReportNextStart) { + // This loader has retained its data, either completely across + // a configuration change or just whatever the last data set + // was after being restarted from a stop, and now at the point of + // finishing the retain we find we remain started, have + // our data, and the owner has a new callback... so + // let's deliver the data now. + callOnLoadFinished(mLoader, mData); + } + } + + void reportStart() { + if (mStarted) { + if (mReportNextStart) { + mReportNextStart = false; + if (mHaveData) { + callOnLoadFinished(mLoader, mData); + } + } + } + } + + void stop() { + if (DEBUG) Log.v(TAG, " Stopping: " + this); + mStarted = false; + if (!mRetaining) { + if (mLoader != null && mListenerRegistered) { + // Let the loader know we're done with it + mListenerRegistered = false; + mLoader.unregisterListener(this); + mLoader.stopLoading(); + } + } + } + + void destroy() { + if (DEBUG) Log.v(TAG, " Destroying: " + this); + mDestroyed = true; + boolean needReset = mDeliveredData; + mDeliveredData = false; + if (mCallbacks != null && mLoader != null && mHaveData && needReset) { + if (DEBUG) Log.v(TAG, " Reseting: " + this); + String lastBecause = null; + if (mActivity != null) { + lastBecause = mActivity.getInternalCallbacks().getFragments().mNoTransactionsBecause; + mActivity.getInternalCallbacks().getFragments().mNoTransactionsBecause = "onLoaderReset"; + } + try { + mCallbacks.onLoaderReset(mLoader); + } finally { + if (mActivity != null) { + mActivity.getInternalCallbacks().getFragments().mNoTransactionsBecause = lastBecause; + } + } + } + mCallbacks = null; + mData = null; + mHaveData = false; + if (mLoader != null) { + if (mListenerRegistered) { + mListenerRegistered = false; + mLoader.unregisterListener(this); + } + mLoader.reset(); + } + if (mPendingLoader != null) { + mPendingLoader.destroy(); + } + } + + @Override public void onLoadComplete(Loader loader, Object data) { + if (DEBUG) Log.v(TAG, "onLoadComplete: " + this); + + if (mDestroyed) { + if (DEBUG) Log.v(TAG, " Ignoring load complete -- destroyed"); + return; + } + + if (mLoaders.get(mId) != this) { + // This data is not coming from the current active loader. + // We don't care about it. + if (DEBUG) Log.v(TAG, " Ignoring load complete -- not active"); + return; + } + + LoaderInfo pending = mPendingLoader; + if (pending != null) { + // There is a new request pending and we were just + // waiting for the old one to complete before starting + // it. So now it is time, switch over to the new loader. + if (DEBUG) Log.v(TAG, " Switching to pending loader: " + pending); + mPendingLoader = null; + mLoaders.put(mId, null); + destroy(); + installLoader(pending); + return; + } + + // Notify of the new data so the app can switch out the old data before + // we try to destroy it. + if (mData != data || !mHaveData) { + mData = data; + mHaveData = true; + if (mStarted) { + callOnLoadFinished(loader, data); + } + } + + //if (DEBUG) Log.v(TAG, " onLoadFinished returned: " + this); + + // We have now given the application the new loader with its + // loaded data, so it should have stopped using the previous + // loader. If there is a previous loader on the inactive list, + // clean it up. + LoaderInfo info = mInactiveLoaders.get(mId); + if (info != null && info != this) { + info.mDeliveredData = false; + info.destroy(); + mInactiveLoaders.remove(mId); + } + } + + void callOnLoadFinished(Loader loader, Object data) { + if (mCallbacks != null) { + String lastBecause = null; + if (mActivity != null) { + lastBecause = mActivity.getInternalCallbacks().getFragments().mNoTransactionsBecause; + mActivity.getInternalCallbacks().getFragments().mNoTransactionsBecause = "onLoadFinished"; + } + try { + if (DEBUG) Log.v(TAG, " onLoadFinished in " + loader + ": " + + loader.dataToString(data)); + mCallbacks.onLoadFinished(loader, data); + } finally { + if (mActivity != null) { + mActivity.getInternalCallbacks().getFragments().mNoTransactionsBecause = lastBecause; + } + } + mDeliveredData = true; + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64); + sb.append("LoaderInfo{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" #"); + sb.append(mId); + sb.append(" : "); + DebugUtils.buildShortClassTag(mLoader, sb); + sb.append("}}"); + return sb.toString(); + } + + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + writer.print(prefix); writer.print("mId="); writer.print(mId); + writer.print(" mArgs="); writer.println(mArgs); + writer.print(prefix); writer.print("mCallbacks="); writer.println(mCallbacks); + writer.print(prefix); writer.print("mLoader="); writer.println(mLoader); + if (mLoader != null) { + mLoader.dump(prefix + " ", fd, writer, args); + } + if (mHaveData || mDeliveredData) { + writer.print(prefix); writer.print("mHaveData="); writer.print(mHaveData); + writer.print(" mDeliveredData="); writer.println(mDeliveredData); + writer.print(prefix); writer.print("mData="); writer.println(mData); + } + writer.print(prefix); writer.print("mStarted="); writer.print(mStarted); + writer.print(" mReportNextStart="); writer.print(mReportNextStart); + writer.print(" mDestroyed="); writer.println(mDestroyed); + writer.print(prefix); writer.print("mRetaining="); writer.print(mRetaining); + writer.print(" mRetainingStarted="); writer.print(mRetainingStarted); + writer.print(" mListenerRegistered="); writer.println(mListenerRegistered); + if (mPendingLoader != null) { + writer.print(prefix); writer.println("Pending Loader "); + writer.print(mPendingLoader); writer.println(":"); + mPendingLoader.dump(prefix + " ", fd, writer, args); + } + } + } + + LoaderManagerImpl(SupportActivity activity, boolean started) { + mActivity = activity; + mStarted = started; + } + + void updateActivity(SupportActivity activity) { + mActivity = activity; + } + + private LoaderInfo createLoader(int id, Bundle args, + LoaderManager.LoaderCallbacks callback) { + LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks)callback); + Loader loader = callback.onCreateLoader(id, args); + info.mLoader = (Loader)loader; + return info; + } + + private LoaderInfo createAndInstallLoader(int id, Bundle args, + LoaderManager.LoaderCallbacks callback) { + try { + mCreatingLoader = true; + LoaderInfo info = createLoader(id, args, callback); + installLoader(info); + return info; + } finally { + mCreatingLoader = false; + } + } + + void installLoader(LoaderInfo info) { + mLoaders.put(info.mId, info); + if (mStarted) { + // The activity will start all existing loaders in it's onStart(), + // so only start them here if we're past that point of the activitiy's + // life cycle + info.start(); + } + } + + /** + * Call to initialize a particular ID with a Loader. If this ID already + * has a Loader associated with it, it is left unchanged and any previous + * callbacks replaced with the newly provided ones. If there is not currently + * a Loader for the ID, a new one is created and started. + * + *

This function should generally be used when a component is initializing, + * to ensure that a Loader it relies on is created. This allows it to re-use + * an existing Loader's data if there already is one, so that for example + * when an {@link Activity} is re-created after a configuration change it + * does not need to re-create its loaders. + * + *

Note that in the case where an existing Loader is re-used, the + * args given here will be ignored because you will + * continue using the previous Loader. + * + * @param id A unique (to this LoaderManager instance) identifier under + * which to manage the new Loader. + * @param args Optional arguments that will be propagated to + * {@link LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}. + * @param callback Interface implementing management of this Loader. Required. + * Its onCreateLoader() method will be called while inside of the function to + * instantiate the Loader object. + */ + @SuppressWarnings("unchecked") + public Loader initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks callback) { + if (mCreatingLoader) { + throw new IllegalStateException("Called while creating a loader"); + } + + LoaderInfo info = mLoaders.get(id); + + if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args); + + if (info == null) { + // Loader doesn't already exist; create. + info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks)callback); + if (DEBUG) Log.v(TAG, " Created new loader " + info); + } else { + if (DEBUG) Log.v(TAG, " Re-using existing loader " + info); + info.mCallbacks = (LoaderManager.LoaderCallbacks)callback; + } + + if (info.mHaveData && mStarted) { + // If the loader has already generated its data, report it now. + info.callOnLoadFinished(info.mLoader, info.mData); + } + + return (Loader)info.mLoader; + } + + /** + * Call to re-create the Loader associated with a particular ID. If there + * is currently a Loader associated with this ID, it will be + * canceled/stopped/destroyed as appropriate. A new Loader with the given + * arguments will be created and its data delivered to you once available. + * + *

This function does some throttling of Loaders. If too many Loaders + * have been created for the given ID but not yet generated their data, + * new calls to this function will create and return a new Loader but not + * actually start it until some previous loaders have completed. + * + *

After calling this function, any previous Loaders associated with + * this ID will be considered invalid, and you will receive no further + * data updates from them. + * + * @param id A unique (to this LoaderManager instance) identifier under + * which to manage the new Loader. + * @param args Optional arguments that will be propagated to + * {@link LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}. + * @param callback Interface implementing management of this Loader. Required. + * Its onCreateLoader() method will be called while inside of the function to + * instantiate the Loader object. + */ + @SuppressWarnings("unchecked") + public Loader restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks callback) { + if (mCreatingLoader) { + throw new IllegalStateException("Called while creating a loader"); + } + + LoaderInfo info = mLoaders.get(id); + if (DEBUG) Log.v(TAG, "restartLoader in " + this + ": args=" + args); + if (info != null) { + LoaderInfo inactive = mInactiveLoaders.get(id); + if (inactive != null) { + if (info.mHaveData) { + // This loader now has data... we are probably being + // called from within onLoadComplete, where we haven't + // yet destroyed the last inactive loader. So just do + // that now. + if (DEBUG) Log.v(TAG, " Removing last inactive loader: " + info); + inactive.mDeliveredData = false; + inactive.destroy(); + info.mLoader.abandon(); + mInactiveLoaders.put(id, info); + } else { + // We already have an inactive loader for this ID that we are + // waiting for! What to do, what to do... + if (!info.mStarted) { + // The current Loader has not been started... we thus + // have no reason to keep it around, so bam, slam, + // thank-you-ma'am. + if (DEBUG) Log.v(TAG, " Current loader is stopped; replacing"); + mLoaders.put(id, null); + info.destroy(); + } else { + // Now we have three active loaders... we'll queue + // up this request to be processed once one of the other loaders + // finishes. + if (info.mPendingLoader != null) { + if (DEBUG) Log.v(TAG, " Removing pending loader: " + info.mPendingLoader); + info.mPendingLoader.destroy(); + info.mPendingLoader = null; + } + if (DEBUG) Log.v(TAG, " Enqueuing as new pending loader"); + info.mPendingLoader = createLoader(id, args, + (LoaderManager.LoaderCallbacks)callback); + return (Loader)info.mPendingLoader.mLoader; + } + } + } else { + // Keep track of the previous instance of this loader so we can destroy + // it when the new one completes. + if (DEBUG) Log.v(TAG, " Making last loader inactive: " + info); + info.mLoader.abandon(); + mInactiveLoaders.put(id, info); + } + } + + info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks)callback); + return (Loader)info.mLoader; + } + + /** + * Rip down, tear apart, shred to pieces a current Loader ID. After returning + * from this function, any Loader objects associated with this ID are + * destroyed. Any data associated with them is destroyed. You better not + * be using it when you do this. + * @param id Identifier of the Loader to be destroyed. + */ + public void destroyLoader(int id) { + if (mCreatingLoader) { + throw new IllegalStateException("Called while creating a loader"); + } + + if (DEBUG) Log.v(TAG, "destroyLoader in " + this + " of " + id); + int idx = mLoaders.indexOfKey(id); + if (idx >= 0) { + LoaderInfo info = mLoaders.valueAt(idx); + mLoaders.removeAt(idx); + info.destroy(); + } + idx = mInactiveLoaders.indexOfKey(id); + if (idx >= 0) { + LoaderInfo info = mInactiveLoaders.valueAt(idx); + mInactiveLoaders.removeAt(idx); + info.destroy(); + } + } + + /** + * Return the most recent Loader object associated with the + * given ID. + */ + @SuppressWarnings("unchecked") + public Loader getLoader(int id) { + if (mCreatingLoader) { + throw new IllegalStateException("Called while creating a loader"); + } + + LoaderInfo loaderInfo = mLoaders.get(id); + if (loaderInfo != null) { + if (loaderInfo.mPendingLoader != null) { + return (Loader)loaderInfo.mPendingLoader.mLoader; + } + return (Loader)loaderInfo.mLoader; + } + return null; + } + + void doStart() { + if (DEBUG) Log.v(TAG, "Starting in " + this); + if (mStarted) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Log.w(TAG, "Called doStart when already started: " + this, e); + return; + } + + mStarted = true; + + // Call out to sub classes so they can start their loaders + // Let the existing loaders know that we want to be notified when a load is complete + for (int i = mLoaders.size()-1; i >= 0; i--) { + mLoaders.valueAt(i).start(); + } + } + + void doStop() { + if (DEBUG) Log.v(TAG, "Stopping in " + this); + if (!mStarted) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Log.w(TAG, "Called doStop when not started: " + this, e); + return; + } + + for (int i = mLoaders.size()-1; i >= 0; i--) { + mLoaders.valueAt(i).stop(); + } + mStarted = false; + } + + void doRetain() { + if (DEBUG) Log.v(TAG, "Retaining in " + this); + if (!mStarted) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Log.w(TAG, "Called doRetain when not started: " + this, e); + return; + } + + mRetaining = true; + mStarted = false; + for (int i = mLoaders.size()-1; i >= 0; i--) { + mLoaders.valueAt(i).retain(); + } + } + + void finishRetain() { + if (mRetaining) { + if (DEBUG) Log.v(TAG, "Finished Retaining in " + this); + + mRetaining = false; + for (int i = mLoaders.size()-1; i >= 0; i--) { + mLoaders.valueAt(i).finishRetain(); + } + } + } + + void doReportNextStart() { + for (int i = mLoaders.size()-1; i >= 0; i--) { + mLoaders.valueAt(i).mReportNextStart = true; + } + } + + void doReportStart() { + for (int i = mLoaders.size()-1; i >= 0; i--) { + mLoaders.valueAt(i).reportStart(); + } + } + + void doDestroy() { + if (!mRetaining) { + if (DEBUG) Log.v(TAG, "Destroying Active in " + this); + for (int i = mLoaders.size()-1; i >= 0; i--) { + mLoaders.valueAt(i).destroy(); + } + } + + if (DEBUG) Log.v(TAG, "Destroying Inactive in " + this); + for (int i = mInactiveLoaders.size()-1; i >= 0; i--) { + mInactiveLoaders.valueAt(i).destroy(); + } + mInactiveLoaders.clear(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("LoaderManager{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" in "); + DebugUtils.buildShortClassTag(mActivity, sb); + sb.append("}}"); + return sb.toString(); + } + + @Override + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + if (mLoaders.size() > 0) { + writer.print(prefix); writer.println("Active Loaders:"); + String innerPrefix = prefix + " "; + for (int i=0; i < mLoaders.size(); i++) { + LoaderInfo li = mLoaders.valueAt(i); + writer.print(prefix); writer.print(" #"); writer.print(mLoaders.keyAt(i)); + writer.print(": "); writer.println(li.toString()); + li.dump(innerPrefix, fd, writer, args); + } + } + if (mInactiveLoaders.size() > 0) { + writer.print(prefix); writer.println("Inactive Loaders:"); + String innerPrefix = prefix + " "; + for (int i=0; i < mInactiveLoaders.size(); i++) { + LoaderInfo li = mInactiveLoaders.valueAt(i); + writer.print(prefix); writer.print(" #"); writer.print(mInactiveLoaders.keyAt(i)); + writer.print(": "); writer.println(li.toString()); + li.dump(innerPrefix, fd, writer, args); + } + } + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/app/NoSaveStateFrameLayout.java b/actionbarsherlock/library/src/android/support/v4/app/NoSaveStateFrameLayout.java new file mode 100644 index 000000000..a2177e32b --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/NoSaveStateFrameLayout.java @@ -0,0 +1,63 @@ +/* + * 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 android.support.v4.app; + +import android.content.Context; +import android.os.Parcelable; +import android.util.SparseArray; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +/** + * Pre-Honeycomb versions of the platform don't have View.setSaveFromParentEnabled(), + * so instead we insert this between the view and its parent. + */ +public class NoSaveStateFrameLayout extends FrameLayout { + static ViewGroup wrap(View child) { + NoSaveStateFrameLayout wrapper = new NoSaveStateFrameLayout(child.getContext()); + ViewGroup.LayoutParams childParams = child.getLayoutParams(); + if (childParams != null) { + wrapper.setLayoutParams(childParams); + } + NoSaveStateFrameLayout.LayoutParams lp = new NoSaveStateFrameLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT); + child.setLayoutParams(lp); + wrapper.addView(child); + return wrapper; + } + + public NoSaveStateFrameLayout(Context context) { + super(context); + } + + /** + * Override to prevent freezing of any child views. + */ + @Override + protected void dispatchSaveInstanceState(SparseArray container) { + dispatchFreezeSelfOnly(container); + } + + /** + * Override to prevent thawing of any child views. + */ + @Override + protected void dispatchRestoreInstanceState(SparseArray container) { + dispatchThawSelfOnly(container); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/app/SuperNotCalledException.java b/actionbarsherlock/library/src/android/support/v4/app/SuperNotCalledException.java new file mode 100644 index 000000000..d306b9651 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/SuperNotCalledException.java @@ -0,0 +1,27 @@ +/* + * 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 android.support.v4.app; + +import android.util.AndroidRuntimeException; + +final class SuperNotCalledException extends AndroidRuntimeException { + private static final long serialVersionUID = -5247191382770859874L; + + public SuperNotCalledException(String msg) { + super(msg); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/app/SupportActivity.java b/actionbarsherlock/library/src/android/support/v4/app/SupportActivity.java new file mode 100644 index 000000000..7bc29ff2d --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/app/SupportActivity.java @@ -0,0 +1,299 @@ +package android.support.v4.app; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; + +import com.actionbarsherlock.internal.app.SherlockActivity; + +import android.app.Activity; +import android.app.Application; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +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.Resources; +import android.content.res.TypedArray; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.v4.view.ActionMode; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.util.AttributeSet; +import android.view.ContextMenu; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityEvent; + +/** + *

Instances of this interface represent an activity provided by the support + * library (e.g., {@link FragmentActivity}).

+ * + *

Provided are all of the methods which would be available if you were + * accessing the underlying activity directly and you can safely assume that + * any instances of this interface can be cast to an {@link Activity}. It is + * preferred, however, that you call {@link #asActivity()} instead.

+ */ +public interface SupportActivity extends SherlockActivity { + public static abstract class InternalCallbacks { + abstract void ensureSupportActionBarAttached(); + abstract Handler getHandler(); + abstract FragmentManagerImpl getFragments(); + abstract LoaderManagerImpl getLoaderManager(int index, boolean started, boolean create); + abstract void invalidateSupportFragmentIndex(int index); + abstract boolean getRetaining(); + } + + InternalCallbacks getInternalCallbacks(); + Activity asActivity(); + + /*** Activity methods ***/ + void addContentView(View view, ViewGroup.LayoutParams params); + void closeContextMenu(); + void closeOptionsMenu(); + PendingIntent createPendingResult(int requestCode, Intent data, int flags); + void dismissDialog(int id); + boolean dispatchKeyEvent(KeyEvent event); + boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event); + boolean dispatchTouchEvent(MotionEvent ev); + boolean dispatchTrackballEvent(MotionEvent ev); + View findViewById(int id); + void finish(); + void finishActivity(int requestCode); + void finishActivityFromChild(Activity child, int requestCode); + void finishFromChild(Activity child); + Application getApplication(); + ComponentName getCallingActivity(); + String getCallingPackage(); + int getChangingConfigurations(); + ComponentName getComponentName(); + View getCurrentFocus(); + Intent getIntent(); + Object getLastNonConfigurationInstance(); + LayoutInflater getLayoutInflater(); + String getLocalClassName(); + MenuInflater getMenuInflater(); + Activity getParent(); + SharedPreferences getPreferences(int mode); + int getRequestedOrientation(); + Object getSystemService(String name); + int getTaskId(); + CharSequence getTitle(); + int getTitleColor(); + int getVolumeControlStream(); + Window getWindow(); + WindowManager getWindowManager(); + boolean hasWindowFocus(); + boolean isChild(); + boolean isFinishing(); + boolean isTaskRoot(); + Cursor managedQuery(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder); + boolean moveTaskToBack(boolean nonRoot); + void onConfigurationChanged(Configuration newConfig); + void onContentChanged(); + boolean onContextItemSelected(android.view.MenuItem item); + void onContextMenuClosed(android.view.Menu menu); + void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo); + CharSequence onCreateDescription(); + boolean onCreateOptionsMenu(android.view.Menu menu); + boolean onCreatePanelMenu(int featureId, android.view.Menu menu); + View onCreatePanelView(int featureId); + boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas); + View onCreateView(String name, Context context, AttributeSet attrs); + boolean onKeyDown(int keyCode, KeyEvent event); + boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event); + boolean onKeyUp(int keyCode, KeyEvent event); + void onLowMemory(); + boolean onMenuItemSelected(int featureId, android.view.MenuItem item); + boolean onMenuOpened(int featureId, android.view.Menu menu); + boolean onOptionsItemSelected(android.view.MenuItem item); + void onOptionsMenuClosed(android.view.Menu menu); + void onPanelClosed(int featureId, android.view.Menu menu); + boolean onPrepareOptionsMenu(android.view.Menu menu); + boolean onPreparePanel(int featureId, View view, android.view.Menu menu); + Object onRetainNonConfigurationInstance(); + boolean onSearchRequested(); + boolean onTouchEvent(MotionEvent event); + boolean onTrackballEvent(MotionEvent event); + void onUserInteraction(); + void onWindowAttributesChanged(WindowManager.LayoutParams params); + void onWindowFocusChanged(boolean hasFocus); + void openContextMenu(View view); + void openOptionsMenu(); + void registerForContextMenu(View view); + void removeDialog(int id); + boolean requestWindowFeature(int featureId); + void runOnUiThread(Runnable action); + void setContentView(int layoutResId); + void setContentView(View view); + void setContentView(View view, ViewGroup.LayoutParams params); + void setDefaultKeyMode(int mode); + void setFeatureDrawable(int featureId, Drawable drawable); + void setFeatureDrawableAlpha(int featureId, int alpha); + void setFeatureDrawableResource(int featureId, int resId); + void setFeatureDrawableUri(int featureId, Uri uri); + void setIntent(Intent newIntent); + void setProgress(int progress); + void setProgressBarIndeterminate(boolean indeterminate); + void setProgressBarIndeterminateVisibility(boolean visible); + void setProgressBarVisibility(boolean visible); + void setRequestedOrientation(int requestedOrientation); + void setResult(int resultCode); + void setResult(int resultCode, Intent data); + void setSecondaryProgress(int secondaryProgress); + void setTitle(int titleId); + void setTitle(CharSequence title); + void setTitleColor(int textColor); + void setVisible(boolean visible); + void setVolumeControlStream(int streamType); + void showDialog(int id); + void startActivity(Intent intent); + void startActivityForResult(Intent intent, int requestCode); + void startActivityFromChild(Activity child, Intent intent, int requestCode); + boolean startActivityIfNeeded(Intent intent, int requestCode); + void startManagingCursor(Cursor c); + boolean startNextMatchingActivity(Intent intent); + void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchDate, boolean globalSearch); + void stopManagingCursor(Cursor c); + void takeKeyEvents(boolean get); + void unregisterForContextMenu(View view); + + /*** ContextThemeWrapper methods ***/ + //Object getSystemService(String name); + Resources.Theme getTheme(); + void setTheme(int resId); + + /*** ContextWrapper methods ***/ + //void attachBaseContext(Context base); + boolean bindService(Intent service, ServiceConnection conn, int flags); + int checkCallingOrSelfPermission(String permission); + int checkCallingOrSelfUriPermission(Uri uri, int modeFlags); + int checkCallingPermission(String permission); + int checkCallingUriPermission(Uri uri, int modeFlags); + int checkPermission(String permission, int pid, int uid); + int checkUriPermission(Uri uri, int pid, int uid, int modeFlags); + int checkUriPermission(Uri uri, String readPermission, String writePermission, int pid, int uid, int modeFlags); + @Deprecated void clearWallpaper() throws IOException; + Context createPackageContext(String packageName, int flags) throws NameNotFoundException; + String[] databaseList(); + boolean deleteDatabase(String name); + boolean deleteFile(String name); + void enforceCallingOrSelfPermission(String permission, String message); + void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message); + void enforceCallingPermission(String permission, String message); + void enforceCallingUriPermission(Uri uri, int modeFlags, String message); + void enforcePermission(String permission, int pid, int uid, String message); + void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message); + void enforceUriPermission(Uri uri, String readPermission, String writePermission, int pid, int uid, int modeFlags, String message); + String[] fileList(); + Context getApplicationContext(); + ApplicationInfo getApplicationInfo(); + AssetManager getAssets(); + Context getBaseContext(); + File getCacheDir(); + ClassLoader getClassLoader(); + ContentResolver getContentResolver(); + File getDatabasePath(String name); + File getDir(String name, int mode); + File getFileStreamPath(String name); + File getFilesDir(); + Looper getMainLooper(); + String getPackageCodePath(); + PackageManager getPackageManager(); + String getPackageName(); + String getPackageResourcePath(); + Resources getResources(); + SharedPreferences getSharedPreferences(String name, int mode); + //Object getSystemService(String name); + //Resources.Theme getTheme(); + Drawable getWallpaper(); + int getWallpaperDesiredMinimumHeight(); + int getWallpaperDesiredMinimumWidth(); + void grantUriPermission(String toPackage, Uri uri, int modeFlags); + boolean isRestricted(); + FileInputStream openFileInput(String name) throws FileNotFoundException; + FileOutputStream openFileOutput(String name, int mode) throws FileNotFoundException; + SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory); + @Deprecated Drawable peekWallpaper(); + Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter); + Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler); + void removeStickyBroadcast(Intent intent); + void revokeUriPermission(Uri uri, int modeFlags); + void sendBroadcast(Intent intent); + void sendBroadcast(Intent intent, String receiverPermission); + void sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras); + void sendOrderedBroadcast(Intent intent, String receiverPermission); + void sendStickyBroadcast(Intent intent); + //void setTheme(int resid); + void setWallpaper(Bitmap bitmap) throws IOException; + void setWallpaper(InputStream data) throws IOException; + //void startActivity(Intent intent); + boolean startInstrumentation(ComponentName className, String profileFile, Bundle arguments); + ComponentName startService(Intent service); + boolean stopService(Intent name); + void unbindService(ServiceConnection conn); + void unregisterReceiver(BroadcastReceiver receiver); + + /*** Context methods ***/ + String getString(int resId); + String getString(int resId, Object... formatArgs); + CharSequence getText(int resId); + //boolean isRestricted(); + TypedArray obtainStyledAttributes(int[] attrs); + TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs); + TypedArray obtainStyledAttributes(int resId, int[] attrs); + TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes); + + /*** Activity methods (emulated API 5+) ***/ + void onBackPressed(); + + /*** Activity methods (emulated API 11+) ***/ + void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); + ActionBar getSupportActionBar(); //getActionBar() + FragmentManager getSupportFragmentManager(); //getFragmentManager() + LoaderManager getSupportLoaderManager(); //getLoaderManager() + void invalidateOptionsMenu(); + void onActionModeFinished(ActionMode mode); + void onActionModeStarted(ActionMode mode); + void onAttachFragment(Fragment fragment); + boolean onCreateOptionsMenu(Menu menu); + boolean onMenuItemSelected(int featureId, MenuItem item); + boolean onOptionsItemSelected(MenuItem item); + boolean onPrepareOptionsMenu(Menu menu); + ActionMode onWindowStartingActionMode(ActionMode.Callback callback); + void recreate(); + ActionMode startActionMode(ActionMode.Callback callback); + void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode); + + /*** Parallel helper methods ***/ + boolean requestWindowFeature(long featureId); + void setProgressBarIndeterminateVisibility(Boolean visible); +} diff --git a/actionbarsherlock/library/src/android/support/v4/content/AsyncTaskLoader.java b/actionbarsherlock/library/src/android/support/v4/content/AsyncTaskLoader.java new file mode 100644 index 000000000..e52d0893b --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/content/AsyncTaskLoader.java @@ -0,0 +1,284 @@ +/* + * 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 android.support.v4.content; + +import android.content.Context; +import android.os.Handler; +import android.os.SystemClock; +import android.support.v4.util.TimeUtils; +import android.util.Log; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.concurrent.CountDownLatch; + +/** + * Static library support version of the framework's {@link android.content.AsyncTaskLoader}. + * Used to write apps that run on platforms prior to Android 3.0. When running + * on Android 3.0 or above, this implementation is still used; it does not try + * to switch to the framework's implementation. See the framework SDK + * documentation for a class overview. + */ +public abstract class AsyncTaskLoader extends Loader { + static final String TAG = "AsyncTaskLoader"; + static final boolean DEBUG = false; + + final class LoadTask extends ModernAsyncTask implements Runnable { + + D result; + boolean waiting; + + private CountDownLatch done = new CountDownLatch(1); + + /* Runs on a worker thread */ + @Override + protected D doInBackground(Void... params) { + if (DEBUG) Log.v(TAG, this + " >>> doInBackground"); + result = AsyncTaskLoader.this.onLoadInBackground(); + if (DEBUG) Log.v(TAG, this + " <<< doInBackground"); + return result; + } + + /* Runs on the UI thread */ + @Override + protected void onPostExecute(D data) { + if (DEBUG) Log.v(TAG, this + " onPostExecute"); + try { + AsyncTaskLoader.this.dispatchOnLoadComplete(this, data); + } finally { + done.countDown(); + } + } + + @Override + protected void onCancelled() { + if (DEBUG) Log.v(TAG, this + " onCancelled"); + try { + AsyncTaskLoader.this.dispatchOnCancelled(this, result); + } finally { + done.countDown(); + } + } + + @Override + public void run() { + waiting = false; + AsyncTaskLoader.this.executePendingTask(); + } + } + + volatile LoadTask mTask; + volatile LoadTask mCancellingTask; + + long mUpdateThrottle; + long mLastLoadCompleteTime = -10000; + Handler mHandler; + + public AsyncTaskLoader(Context context) { + super(context); + } + + /** + * Set amount to throttle updates by. This is the minimum time from + * when the last {@link #onLoadInBackground()} call has completed until + * a new load is scheduled. + * + * @param delayMS Amount of delay, in milliseconds. + */ + public void setUpdateThrottle(long delayMS) { + mUpdateThrottle = delayMS; + if (delayMS != 0) { + mHandler = new Handler(); + } + } + + @Override + protected void onForceLoad() { + super.onForceLoad(); + cancelLoad(); + mTask = new LoadTask(); + if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask); + executePendingTask(); + } + + /** + * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)} + * for more info. Must be called on the main thread of the process. + * + *

Cancelling is not an immediate operation, since the load is performed + * in a background thread. If there is currently a load in progress, this + * method requests that the load be cancelled, and notes this is the case; + * once the background thread has completed its work its remaining state + * will be cleared. If another load request comes in during this time, + * it will be held until the cancelled load is complete. + * + * @return Returns false if the task could not be cancelled, + * typically because it has already completed normally, or + * because {@link #startLoading()} hasn't been called; returns + * true otherwise. + */ + public boolean cancelLoad() { + if (DEBUG) Log.v(TAG, "cancelLoad: mTask=" + mTask); + if (mTask != null) { + if (mCancellingTask != null) { + // There was a pending task already waiting for a previous + // one being canceled; just drop it. + if (DEBUG) Log.v(TAG, + "cancelLoad: still waiting for cancelled task; dropping next"); + if (mTask.waiting) { + mTask.waiting = false; + mHandler.removeCallbacks(mTask); + } + mTask = null; + return false; + } else if (mTask.waiting) { + // There is a task, but it is waiting for the time it should + // execute. We can just toss it. + if (DEBUG) Log.v(TAG, "cancelLoad: task is waiting, dropping it"); + mTask.waiting = false; + mHandler.removeCallbacks(mTask); + mTask = null; + return false; + } else { + boolean cancelled = mTask.cancel(false); + if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled); + if (cancelled) { + mCancellingTask = mTask; + } + mTask = null; + return cancelled; + } + } + return false; + } + + /** + * Called if the task was canceled before it was completed. Gives the class a chance + * to properly dispose of the result. + */ + public void onCanceled(D data) { + } + + void executePendingTask() { + if (mCancellingTask == null && mTask != null) { + if (mTask.waiting) { + mTask.waiting = false; + mHandler.removeCallbacks(mTask); + } + if (mUpdateThrottle > 0) { + long now = SystemClock.uptimeMillis(); + if (now < (mLastLoadCompleteTime+mUpdateThrottle)) { + // Not yet time to do another load. + if (DEBUG) Log.v(TAG, "Waiting until " + + (mLastLoadCompleteTime+mUpdateThrottle) + + " to execute: " + mTask); + mTask.waiting = true; + mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle); + return; + } + } + if (DEBUG) Log.v(TAG, "Executing: " + mTask); + mTask.executeOnExecutor(ModernAsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); + } + } + + void dispatchOnCancelled(LoadTask task, D data) { + onCanceled(data); + if (mCancellingTask == task) { + if (DEBUG) Log.v(TAG, "Cancelled task is now canceled!"); + mLastLoadCompleteTime = SystemClock.uptimeMillis(); + mCancellingTask = null; + executePendingTask(); + } + } + + void dispatchOnLoadComplete(LoadTask task, D data) { + if (mTask != task) { + if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel"); + dispatchOnCancelled(task, data); + } else { + if (isAbandoned()) { + // This cursor has been abandoned; just cancel the new data. + onCanceled(data); + } else { + mLastLoadCompleteTime = SystemClock.uptimeMillis(); + mTask = null; + if (DEBUG) Log.v(TAG, "Delivering result"); + deliverResult(data); + } + } + } + + /** + */ + public abstract D loadInBackground(); + + /** + * Called on a worker thread to perform the actual load. Implementations should not deliver the + * result directly, but should return them from this method, which will eventually end up + * calling {@link #deliverResult} on the UI thread. If implementations need to process + * the results on the UI thread they may override {@link #deliverResult} and do so + * there. + * + * @return Implementations must return the result of their load operation. + */ + protected D onLoadInBackground() { + return loadInBackground(); + } + + /** + * Locks the current thread until the loader completes the current load + * operation. Returns immediately if there is no load operation running. + * Should not be called from the UI thread: calling it from the UI + * thread would cause a deadlock. + *

+ * Use for testing only. Never call this from a UI thread. + * + * @hide + */ + public void waitForLoader() { + LoadTask task = mTask; + if (task != null) { + try { + task.done.await(); + } catch (InterruptedException e) { + // Ignore + } + } + } + + @Override + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + super.dump(prefix, fd, writer, args); + if (mTask != null) { + writer.print(prefix); writer.print("mTask="); writer.print(mTask); + writer.print(" waiting="); writer.println(mTask.waiting); + } + if (mCancellingTask != null) { + writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask); + writer.print(" waiting="); writer.println(mCancellingTask.waiting); + } + if (mUpdateThrottle != 0) { + writer.print(prefix); writer.print("mUpdateThrottle="); + TimeUtils.formatDuration(mUpdateThrottle, writer); + writer.print(" mLastLoadCompleteTime="); + TimeUtils.formatDuration(mLastLoadCompleteTime, + SystemClock.uptimeMillis(), writer); + writer.println(); + } + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/content/CursorLoader.java b/actionbarsherlock/library/src/android/support/v4/content/CursorLoader.java new file mode 100644 index 000000000..51ad8b54e --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/content/CursorLoader.java @@ -0,0 +1,215 @@ +/* + * 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 android.support.v4.content; + +import android.content.Context; +import android.database.ContentObserver; +import android.database.Cursor; +import android.net.Uri; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Arrays; + +/** + * Static library support version of the framework's {@link android.content.CursorLoader}. + * Used to write apps that run on platforms prior to Android 3.0. When running + * on Android 3.0 or above, this implementation is still used; it does not try + * to switch to the framework's implementation. See the framework SDK + * documentation for a class overview. + */ +public class CursorLoader extends AsyncTaskLoader { + final ForceLoadContentObserver mObserver; + + Uri mUri; + String[] mProjection; + String mSelection; + String[] mSelectionArgs; + String mSortOrder; + + Cursor mCursor; + + /* Runs on a worker thread */ + @Override + public Cursor loadInBackground() { + Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection, + mSelectionArgs, mSortOrder); + if (cursor != null) { + // Ensure the cursor window is filled + cursor.getCount(); + registerContentObserver(cursor, mObserver); + } + return cursor; + } + + /** + * Registers an observer to get notifications from the content provider + * when the cursor needs to be refreshed. + */ + void registerContentObserver(Cursor cursor, ContentObserver observer) { + cursor.registerContentObserver(mObserver); + } + + /* Runs on the UI thread */ + @Override + public void deliverResult(Cursor cursor) { + if (isReset()) { + // An async query came in while the loader is stopped + if (cursor != null) { + cursor.close(); + } + return; + } + Cursor oldCursor = mCursor; + mCursor = cursor; + + if (isStarted()) { + super.deliverResult(cursor); + } + + if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) { + oldCursor.close(); + } + } + + /** + * Creates an empty unspecified CursorLoader. You must follow this with + * calls to {@link #setUri(Uri)}, {@link #setSelection(String)}, etc + * to specify the query to perform. + */ + public CursorLoader(Context context) { + super(context); + mObserver = new ForceLoadContentObserver(); + } + + /** + * Creates a fully-specified CursorLoader. See + * {@link ContentResolver#query(Uri, String[], String, String[], String) + * ContentResolver.query()} for documentation on the meaning of the + * parameters. These will be passed as-is to that call. + */ + public CursorLoader(Context context, Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + super(context); + mObserver = new ForceLoadContentObserver(); + mUri = uri; + mProjection = projection; + mSelection = selection; + mSelectionArgs = selectionArgs; + mSortOrder = sortOrder; + } + + /** + * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks + * will be called on the UI thread. If a previous load has been completed and is still valid + * the result may be passed to the callbacks immediately. + * + * Must be called from the UI thread + */ + @Override + protected void onStartLoading() { + if (mCursor != null) { + deliverResult(mCursor); + } + if (takeContentChanged() || mCursor == null) { + forceLoad(); + } + } + + /** + * Must be called from the UI thread + */ + @Override + protected void onStopLoading() { + // Attempt to cancel the current load task if possible. + cancelLoad(); + } + + @Override + public void onCanceled(Cursor cursor) { + if (cursor != null && !cursor.isClosed()) { + cursor.close(); + } + } + + @Override + protected void onReset() { + super.onReset(); + + // Ensure the loader is stopped + onStopLoading(); + + if (mCursor != null && !mCursor.isClosed()) { + mCursor.close(); + } + mCursor = null; + } + + public Uri getUri() { + return mUri; + } + + public void setUri(Uri uri) { + mUri = uri; + } + + public String[] getProjection() { + return mProjection; + } + + public void setProjection(String[] projection) { + mProjection = projection; + } + + public String getSelection() { + return mSelection; + } + + public void setSelection(String selection) { + mSelection = selection; + } + + public String[] getSelectionArgs() { + return mSelectionArgs; + } + + public void setSelectionArgs(String[] selectionArgs) { + mSelectionArgs = selectionArgs; + } + + public String getSortOrder() { + return mSortOrder; + } + + public void setSortOrder(String sortOrder) { + mSortOrder = sortOrder; + } + + @Override + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + super.dump(prefix, fd, writer, args); + writer.print(prefix); writer.print("mUri="); writer.println(mUri); + writer.print(prefix); writer.print("mProjection="); + writer.println(Arrays.toString(mProjection)); + writer.print(prefix); writer.print("mSelection="); writer.println(mSelection); + writer.print(prefix); writer.print("mSelectionArgs="); + writer.println(Arrays.toString(mSelectionArgs)); + writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder); + writer.print(prefix); writer.print("mCursor="); writer.println(mCursor); + writer.print(prefix); writer.print("mContentChanged="); writer.println(mContentChanged); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/content/Loader.java b/actionbarsherlock/library/src/android/support/v4/content/Loader.java new file mode 100644 index 000000000..b9f2c6a87 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/content/Loader.java @@ -0,0 +1,358 @@ +/* + * 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 android.support.v4.content; + +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.support.v4.util.DebugUtils; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * Static library support version of the framework's {@link android.content.Loader}. + * Used to write apps that run on platforms prior to Android 3.0. When running + * on Android 3.0 or above, this implementation is still used; it does not try + * to switch to the framework's implementation. See the framework SDK + * documentation for a class overview. + */ +public class Loader { + int mId; + OnLoadCompleteListener mListener; + Context mContext; + boolean mStarted = false; + boolean mAbandoned = false; + boolean mReset = true; + boolean mContentChanged = false; + + public final class ForceLoadContentObserver extends ContentObserver { + public ForceLoadContentObserver() { + super(new Handler()); + } + + @Override + public boolean deliverSelfNotifications() { + return true; + } + + @Override + public void onChange(boolean selfChange) { + onContentChanged(); + } + } + + public interface OnLoadCompleteListener { + /** + * Called on the thread that created the Loader when the load is complete. + * + * @param loader the loader that completed the load + * @param data the result of the load + */ + public void onLoadComplete(Loader loader, D data); + } + + /** + * Stores away the application context associated with context. Since Loaders can be used + * across multiple activities it's dangerous to store the context directly. + * + * @param context used to retrieve the application context. + */ + public Loader(Context context) { + mContext = context.getApplicationContext(); + } + + /** + * Sends the result of the load to the registered listener. Should only be called by subclasses. + * + * Must be called from the process's main thread. + * + * @param data the result of the load + */ + public void deliverResult(D data) { + if (mListener != null) { + mListener.onLoadComplete(this, data); + } + } + + /** + * @return an application context retrieved from the Context passed to the constructor. + */ + public Context getContext() { + return mContext; + } + + /** + * @return the ID of this loader + */ + public int getId() { + return mId; + } + + /** + * Registers a class that will receive callbacks when a load is complete. + * The callback will be called on the process's main thread so it's safe to + * pass the results to widgets. + * + *

Must be called from the process's main thread. + */ + public void registerListener(int id, OnLoadCompleteListener listener) { + if (mListener != null) { + throw new IllegalStateException("There is already a listener registered"); + } + mListener = listener; + mId = id; + } + + /** + * Remove a listener that was previously added with {@link #registerListener}. + * + * Must be called from the process's main thread. + */ + public void unregisterListener(OnLoadCompleteListener listener) { + if (mListener == null) { + throw new IllegalStateException("No listener register"); + } + if (mListener != listener) { + throw new IllegalArgumentException("Attempting to unregister the wrong listener"); + } + mListener = null; + } + + /** + * Return whether this load has been started. That is, its {@link #startLoading()} + * has been called and no calls to {@link #stopLoading()} or + * {@link #reset()} have yet been made. + */ + public boolean isStarted() { + return mStarted; + } + + /** + * Return whether this loader has been abandoned. In this state, the + * loader must not report any new data, and must keep + * its last reported data valid until it is finally reset. + */ + public boolean isAbandoned() { + return mAbandoned; + } + + /** + * Return whether this load has been reset. That is, either the loader + * has not yet been started for the first time, or its {@link #reset()} + * has been called. + */ + public boolean isReset() { + return mReset; + } + + /** + * Starts an asynchronous load of the Loader's data. When the result + * is ready the callbacks will be called on the process's main thread. + * If a previous load has been completed and is still valid + * the result may be passed to the callbacks immediately. + * The loader will monitor the source of + * the data set and may deliver future callbacks if the source changes. + * Calling {@link #stopLoading} will stop the delivery of callbacks. + * + *

This updates the Loader's internal state so that + * {@link #isStarted()} and {@link #isReset()} will return the correct + * values, and then calls the implementation's {@link #onStartLoading()}. + * + *

Must be called from the process's main thread. + */ + public final void startLoading() { + mStarted = true; + mReset = false; + mAbandoned = false; + onStartLoading(); + } + + /** + * Subclasses must implement this to take care of loading their data, + * as per {@link #startLoading()}. This is not called by clients directly, + * but as a result of a call to {@link #startLoading()}. + */ + protected void onStartLoading() { + } + + /** + * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously + * loaded data set and load a new one. This simply calls through to the + * implementation's {@link #onForceLoad()}. You generally should only call this + * when the loader is started -- that is, {@link #isStarted()} returns true. + * + *

Must be called from the process's main thread. + */ + public void forceLoad() { + onForceLoad(); + } + + /** + * Subclasses must implement this to take care of requests to {@link #forceLoad()}. + * This will always be called from the process's main thread. + */ + protected void onForceLoad() { + } + + /** + * Stops delivery of updates until the next time {@link #startLoading()} is called. + * Implementations should not invalidate their data at this point -- + * clients are still free to use the last data the loader reported. They will, + * however, typically stop reporting new data if the data changes; they can + * still monitor for changes, but must not report them to the client until and + * if {@link #startLoading()} is later called. + * + *

This updates the Loader's internal state so that + * {@link #isStarted()} will return the correct + * value, and then calls the implementation's {@link #onStopLoading()}. + * + *

Must be called from the process's main thread. + */ + public void stopLoading() { + mStarted = false; + onStopLoading(); + } + + /** + * Subclasses must implement this to take care of stopping their loader, + * as per {@link #stopLoading()}. This is not called by clients directly, + * but as a result of a call to {@link #stopLoading()}. + * This will always be called from the process's main thread. + */ + protected void onStopLoading() { + } + + /** + * Tell the Loader that it is being abandoned. This is called prior + * to {@link #reset} to have it retain its current data but not report + * any new data. + */ + public void abandon() { + mAbandoned = true; + onAbandon(); + } + + /** + * Subclasses implement this to take care of being abandoned. This is + * an optional intermediate state prior to {@link #onReset()} -- it means that + * the client is no longer interested in any new data from the loader, + * so the loader must not report any further updates. However, the + * loader must keep its last reported data valid until the final + * {@link #onReset()} happens. You can retrieve the current abandoned + * state with {@link #isAbandoned}. + */ + protected void onAbandon() { + } + + /** + * Resets the state of the Loader. The Loader should at this point free + * all of its resources, since it may never be called again; however, its + * {@link #startLoading()} may later be called at which point it must be + * able to start running again. + * + *

This updates the Loader's internal state so that + * {@link #isStarted()} and {@link #isReset()} will return the correct + * values, and then calls the implementation's {@link #onReset()}. + * + *

Must be called from the process's main thread. + */ + public void reset() { + onReset(); + mReset = true; + mStarted = false; + mAbandoned = false; + mContentChanged = false; + } + + /** + * Subclasses must implement this to take care of resetting their loader, + * as per {@link #reset()}. This is not called by clients directly, + * but as a result of a call to {@link #reset()}. + * This will always be called from the process's main thread. + */ + protected void onReset() { + } + + /** + * Take the current flag indicating whether the loader's content had + * changed while it was stopped. If it had, true is returned and the + * flag is cleared. + */ + public boolean takeContentChanged() { + boolean res = mContentChanged; + mContentChanged = false; + return res; + } + + /** + * Called when {@link ForceLoadContentObserver} detects a change. The + * default implementation checks to see if the loader is currently started; + * if so, it simply calls {@link #forceLoad()}; otherwise, it sets a flag + * so that {@link #takeContentChanged()} returns true. + * + *

Must be called from the process's main thread. + */ + public void onContentChanged() { + if (mStarted) { + forceLoad(); + } else { + // This loader has been stopped, so we don't want to load + // new data right now... but keep track of it changing to + // refresh later if we start again. + mContentChanged = true; + } + } + + /** + * For debugging, converts an instance of the Loader's data class to + * a string that can be printed. Must handle a null data. + */ + public String dataToString(D data) { + StringBuilder sb = new StringBuilder(64); + DebugUtils.buildShortClassTag(data, sb); + sb.append("}"); + return sb.toString(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64); + DebugUtils.buildShortClassTag(this, sb); + sb.append(" id="); + sb.append(mId); + sb.append("}"); + return sb.toString(); + } + + /** + * Print the Loader's state into the given stream. + * + * @param prefix Text to print at the front of each line. + * @param fd The raw file descriptor that the dump is being sent to. + * @param writer A PrintWriter to which the dump is to be set. + * @param args Additional arguments to the dump request. + */ + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + writer.print(prefix); writer.print("mId="); writer.print(mId); + writer.print(" mListener="); writer.println(mListener); + writer.print(prefix); writer.print("mStarted="); writer.print(mStarted); + writer.print(" mContentChanged="); writer.print(mContentChanged); + writer.print(" mAbandoned="); writer.print(mAbandoned); + writer.print(" mReset="); writer.println(mReset); + } +} \ No newline at end of file diff --git a/actionbarsherlock/library/src/android/support/v4/content/LocalBroadcastManager.java b/actionbarsherlock/library/src/android/support/v4/content/LocalBroadcastManager.java new file mode 100644 index 000000000..7a7c50f4e --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/content/LocalBroadcastManager.java @@ -0,0 +1,302 @@ +/* + * 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 android.support.v4.content; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Set; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +/** + * Helper to register for and send broadcasts of Intents to local objects + * within your process. This is has a number of advantages over sending + * global broadcasts with {@link android.content.Context#sendBroadcast}: + *

    + *
  • You know that the data you are broadcasting won't leave your app, so + * don't need to worry about leaking private data. + *
  • It is not possible for other applications to send these broadcasts to + * your app, so you don't need to worry about having security holes they can + * exploit. + *
  • It is more efficient than sending a global broadcast through the + * system. + *
+ */ +public class LocalBroadcastManager { + private static class ReceiverRecord { + final IntentFilter filter; + final BroadcastReceiver receiver; + boolean broadcasting; + + ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) { + filter = _filter; + receiver = _receiver; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(128); + builder.append("Receiver{"); + builder.append(receiver); + builder.append(" filter="); + builder.append(filter); + builder.append("}"); + return builder.toString(); + } + } + + private static class BroadcastRecord { + final Intent intent; + final ArrayList receivers; + + BroadcastRecord(Intent _intent, ArrayList _receivers) { + intent = _intent; + receivers = _receivers; + } + } + + private static final String TAG = "LocalBroadcastManager"; + private static final boolean DEBUG = false; + + private final Context mAppContext; + + private final HashMap> mReceivers + = new HashMap>(); + private final HashMap> mActions + = new HashMap>(); + + private final ArrayList mPendingBroadcasts + = new ArrayList(); + + static final int MSG_EXEC_PENDING_BROADCASTS = 1; + + private final Handler mHandler; + + private static final Object mLock = new Object(); + private static LocalBroadcastManager mInstance; + + public static LocalBroadcastManager getInstance(Context context) { + synchronized (mLock) { + if (mInstance == null) { + mInstance = new LocalBroadcastManager(context.getApplicationContext()); + } + return mInstance; + } + } + + private LocalBroadcastManager(Context context) { + mAppContext = context; + mHandler = new Handler(context.getMainLooper()) { + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_EXEC_PENDING_BROADCASTS: + executePendingBroadcasts(); + break; + default: + super.handleMessage(msg); + } + } + }; + } + + /** + * Register a receive for any local broadcasts that match the given IntentFilter. + * + * @param receiver The BroadcastReceiver to handle the broadcast. + * @param filter Selects the Intent broadcasts to be received. + * + * @see #unregisterReceiver + */ + public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { + synchronized (mReceivers) { + ReceiverRecord entry = new ReceiverRecord(filter, receiver); + ArrayList filters = mReceivers.get(receiver); + if (filters == null) { + filters = new ArrayList(1); + mReceivers.put(receiver, filters); + } + filters.add(filter); + for (int i=0; i entries = mActions.get(action); + if (entries == null) { + entries = new ArrayList(1); + mActions.put(action, entries); + } + entries.add(entry); + } + } + } + + /** + * Unregister a previously registered BroadcastReceiver. All + * filters that have been registered for this BroadcastReceiver will be + * removed. + * + * @param receiver The BroadcastReceiver to unregister. + * + * @see #registerReceiver + */ + public void unregisterReceiver(BroadcastReceiver receiver) { + synchronized (mReceivers) { + ArrayList filters = mReceivers.remove(receiver); + if (filters == null) { + return; + } + for (int i=0; i receivers = mActions.get(action); + if (receivers != null) { + for (int k=0; k categories = intent.getCategories(); + + final boolean debug = DEBUG || + ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); + if (debug) Log.v( + TAG, "Resolving type " + type + " scheme " + scheme + + " of intent " + intent); + + ArrayList entries = mActions.get(intent.getAction()); + if (entries != null) { + if (debug) Log.v(TAG, "Action list: " + entries); + + ArrayList receivers = null; + for (int i=0; i= 0) { + if (debug) Log.v(TAG, " Filter matched! match=0x" + + Integer.toHexString(match)); + if (receivers == null) { + receivers = new ArrayList(); + } + receivers.add(receiver); + receiver.broadcasting = true; + } else { + if (debug) { + String reason; + switch (match) { + case IntentFilter.NO_MATCH_ACTION: reason = "action"; break; + case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break; + case IntentFilter.NO_MATCH_DATA: reason = "data"; break; + case IntentFilter.NO_MATCH_TYPE: reason = "type"; break; + default: reason = "unknown reason"; break; + } + Log.v(TAG, " Filter did not match: " + reason); + } + } + } + + if (receivers != null) { + for (int i=0; i { + private static final String LOG_TAG = "AsyncTask"; + + private static final int CORE_POOL_SIZE = 5; + private static final int MAXIMUM_POOL_SIZE = 128; + private static final int KEEP_ALIVE = 1; + + private static final ThreadFactory sThreadFactory = new ThreadFactory() { + private final AtomicInteger mCount = new AtomicInteger(1); + + public Thread newThread(Runnable r) { + return new Thread(r, "ModernAsyncTask #" + mCount.getAndIncrement()); + } + }; + + private static final BlockingQueue sPoolWorkQueue = + new LinkedBlockingQueue(10); + + /** + * An {@link Executor} that can be used to execute tasks in parallel. + */ + public static final Executor THREAD_POOL_EXECUTOR + = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, + TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); + + private static final int MESSAGE_POST_RESULT = 0x1; + private static final int MESSAGE_POST_PROGRESS = 0x2; + + private static final InternalHandler sHandler = new InternalHandler(); + + private static volatile Executor sDefaultExecutor = THREAD_POOL_EXECUTOR; + private final WorkerRunnable mWorker; + private final FutureTask mFuture; + + private volatile Status mStatus = Status.PENDING; + + private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); + + /** + * Indicates the current status of the task. Each status will be set only once + * during the lifetime of a task. + */ + public enum Status { + /** + * Indicates that the task has not been executed yet. + */ + PENDING, + /** + * Indicates that the task is running. + */ + RUNNING, + /** + * Indicates that {@link AsyncTask#onPostExecute} has finished. + */ + FINISHED, + } + + /** @hide Used to force static handler to be created. */ + public static void init() { + sHandler.getLooper(); + } + + /** @hide */ + public static void setDefaultExecutor(Executor exec) { + sDefaultExecutor = exec; + } + + /** + * Creates a new asynchronous task. This constructor must be invoked on the UI thread. + */ + public ModernAsyncTask() { + mWorker = new WorkerRunnable() { + public Result call() throws Exception { + mTaskInvoked.set(true); + + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + return postResult(doInBackground(mParams)); + } + }; + + mFuture = new FutureTask(mWorker) { + @Override + protected void done() { + try { + final Result result = get(); + + postResultIfNotInvoked(result); + } catch (InterruptedException e) { + android.util.Log.w(LOG_TAG, e); + } catch (ExecutionException e) { + throw new RuntimeException("An error occured while executing doInBackground()", + e.getCause()); + } catch (CancellationException e) { + postResultIfNotInvoked(null); + } catch (Throwable t) { + throw new RuntimeException("An error occured while executing " + + "doInBackground()", t); + } + } + }; + } + + private void postResultIfNotInvoked(Result result) { + final boolean wasTaskInvoked = mTaskInvoked.get(); + if (!wasTaskInvoked) { + postResult(result); + } + } + + private Result postResult(Result result) { + @SuppressWarnings("unchecked") + Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, + new AsyncTaskResult(this, result)); + message.sendToTarget(); + return result; + } + + /** + * Returns the current status of this task. + * + * @return The current status. + */ + public final Status getStatus() { + return mStatus; + } + + /** + * Override this method to perform a computation on a background thread. The + * specified parameters are the parameters passed to {@link #execute} + * by the caller of this task. + * + * This method can call {@link #publishProgress} to publish updates + * on the UI thread. + * + * @param params The parameters of the task. + * + * @return A result, defined by the subclass of this task. + * + * @see #onPreExecute() + * @see #onPostExecute + * @see #publishProgress + */ + protected abstract Result doInBackground(Params... params); + + /** + * Runs on the UI thread before {@link #doInBackground}. + * + * @see #onPostExecute + * @see #doInBackground + */ + protected void onPreExecute() { + } + + /** + *

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

+ * + *

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

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

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

+ * + *

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

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

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

+ * + *

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

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

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

+ * + *

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

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

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

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

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

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

This method must be invoked on the UI thread. + * + * @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a + * convenient process-wide thread pool for tasks that are loosely coupled. + * @param params The parameters of the task. + * + * @return This instance of AsyncTask. + * + * @throws IllegalStateException If {@link #getStatus()} returns either + * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. + */ + public final ModernAsyncTask executeOnExecutor(Executor exec, + Params... params) { + if (mStatus != Status.PENDING) { + switch (mStatus) { + case RUNNING: + throw new IllegalStateException("Cannot execute task:" + + " the task is already running."); + case FINISHED: + throw new IllegalStateException("Cannot execute task:" + + " the task has already been executed " + + "(a task can be executed only once)"); + } + } + + mStatus = Status.RUNNING; + + onPreExecute(); + + mWorker.mParams = params; + exec.execute(mFuture); + + return this; + } + + /** + * Convenience version of {@link #execute(Object...)} for use with + * a simple Runnable object. + */ + public static void execute(Runnable runnable) { + sDefaultExecutor.execute(runnable); + } + + /** + * This method can be invoked from {@link #doInBackground} to + * publish updates on the UI thread while the background computation is + * still running. Each call to this method will trigger the execution of + * {@link #onProgressUpdate} on the UI thread. + * + * {@link #onProgressUpdate} will note be called if the task has been + * canceled. + * + * @param values The progress values to update the UI with. + * + * @see #onProgressUpdate + * @see #doInBackground + */ + protected final void publishProgress(Progress... values) { + if (!isCancelled()) { + sHandler.obtainMessage(MESSAGE_POST_PROGRESS, + new AsyncTaskResult(this, values)).sendToTarget(); + } + } + + private void finish(Result result) { + if (isCancelled()) { + onCancelled(result); + } else { + onPostExecute(result); + } + mStatus = Status.FINISHED; + } + + private static class InternalHandler extends Handler { + @SuppressWarnings({"unchecked", "rawtypes"}) + @Override + public void handleMessage(Message msg) { + AsyncTaskResult result = (AsyncTaskResult) msg.obj; + switch (msg.what) { + case MESSAGE_POST_RESULT: + // There is only one result + result.mTask.finish(result.mData[0]); + break; + case MESSAGE_POST_PROGRESS: + result.mTask.onProgressUpdate(result.mData); + break; + } + } + } + + private static abstract class WorkerRunnable implements Callable { + Params[] mParams; + } + + @SuppressWarnings({"rawtypes"}) + private static class AsyncTaskResult { + final ModernAsyncTask mTask; + final Data[] mData; + + AsyncTaskResult(ModernAsyncTask task, Data... data) { + mTask = task; + mData = data; + } + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/os/ParcelableCompat.java b/actionbarsherlock/library/src/android/support/v4/os/ParcelableCompat.java new file mode 100644 index 000000000..714af3374 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/os/ParcelableCompat.java @@ -0,0 +1,48 @@ +/* + * 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 android.support.v4.os; + +import android.os.Parcel; +import android.os.Parcelable; + +public class ParcelableCompat { + public static Parcelable.Creator newCreator( + ParcelableCompatCreatorCallbacks callbacks) { + if (android.os.Build.VERSION.SDK_INT >= 13) { + ParcelableCompatCreatorHoneycombMR2Stub.instantiate(callbacks); + } + return new CompatCreator(callbacks); + } + + static class CompatCreator implements Parcelable.Creator { + final ParcelableCompatCreatorCallbacks mCallbacks; + + public CompatCreator(ParcelableCompatCreatorCallbacks callbacks) { + mCallbacks = callbacks; + } + + @Override + public T createFromParcel(Parcel source) { + return mCallbacks.createFromParcel(source, null); + } + + @Override + public T[] newArray(int size) { + return mCallbacks.newArray(size); + } + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/os/ParcelableCompatCreatorCallbacks.java b/actionbarsherlock/library/src/android/support/v4/os/ParcelableCompatCreatorCallbacks.java new file mode 100644 index 000000000..8678cfb75 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/os/ParcelableCompatCreatorCallbacks.java @@ -0,0 +1,24 @@ +/* + * 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 android.support.v4.os; + +import android.os.Parcel; + +public interface ParcelableCompatCreatorCallbacks { + public T createFromParcel(Parcel in, ClassLoader loader); + public T[] newArray(int size); +} diff --git a/actionbarsherlock/library/src/android/support/v4/os/ParcelableCompatHoneycombMR2.java b/actionbarsherlock/library/src/android/support/v4/os/ParcelableCompatHoneycombMR2.java new file mode 100644 index 000000000..08acb5507 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/os/ParcelableCompatHoneycombMR2.java @@ -0,0 +1,46 @@ +/* + * 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 android.support.v4.os; + +import android.os.Parcel; +import android.os.Parcelable; + +class ParcelableCompatCreatorHoneycombMR2Stub { + static Parcelable.Creator instantiate(ParcelableCompatCreatorCallbacks callbacks) { + return new ParcelableCompatCreatorHoneycombMR2(callbacks); + } +} + +class ParcelableCompatCreatorHoneycombMR2 implements Parcelable.ClassLoaderCreator { + private final ParcelableCompatCreatorCallbacks mCallbacks; + + public ParcelableCompatCreatorHoneycombMR2(ParcelableCompatCreatorCallbacks callbacks) { + mCallbacks = callbacks; + } + + public T createFromParcel(Parcel in) { + return mCallbacks.createFromParcel(in, null); + } + + public T createFromParcel(Parcel in, ClassLoader loader) { + return mCallbacks.createFromParcel(in, loader); + } + + public T[] newArray(int size) { + return mCallbacks.newArray(size); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/util/DebugUtils.java b/actionbarsherlock/library/src/android/support/v4/util/DebugUtils.java new file mode 100644 index 000000000..16c63a9f0 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/util/DebugUtils.java @@ -0,0 +1,40 @@ +/* + * 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 android.support.v4.util; + +/** + * Useful debugging utilities that are not available on all versions of Android. + */ +public class DebugUtils { + public static void buildShortClassTag(Object cls, StringBuilder out) { + if (cls == null) { + out.append("null"); + } else { + String simpleName = cls.getClass().getSimpleName(); + if (simpleName == null || simpleName.length() <= 0) { + simpleName = cls.getClass().getName(); + int end = simpleName.lastIndexOf('.'); + if (end > 0) { + simpleName = simpleName.substring(end+1); + } + } + out.append(simpleName); + out.append('{'); + out.append(Integer.toHexString(System.identityHashCode(cls))); + } + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/util/LogWriter.java b/actionbarsherlock/library/src/android/support/v4/util/LogWriter.java new file mode 100644 index 000000000..3b9d12b04 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/util/LogWriter.java @@ -0,0 +1,72 @@ +/* + * 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 android.support.v4.util; + +import android.util.Log; + +import java.io.Writer; + +/** + * Useful logging utility that is not available on all versions of Android. + */ +public class LogWriter extends Writer { + private final String mTag; + private StringBuilder mBuilder = new StringBuilder(128); + + /** + * Create a new Writer that sends to the log with the given priority + * and tag. + * + * @param priority The desired log priority: + * {@link android.util.Log#VERBOSE Log.VERBOSE}, + * {@link android.util.Log#DEBUG Log.DEBUG}, + * {@link android.util.Log#INFO Log.INFO}, + * {@link android.util.Log#WARN Log.WARN}, or + * {@link android.util.Log#ERROR Log.ERROR}. + * @param tag A string tag to associate with each printed log statement. + */ + public LogWriter(String tag) { + mTag = tag; + } + + @Override public void close() { + flushBuilder(); + } + + @Override public void flush() { + flushBuilder(); + } + + @Override public void write(char[] buf, int offset, int count) { + for(int i = 0; i < count; i++) { + char c = buf[offset + i]; + if ( c == '\n') { + flushBuilder(); + } + else { + mBuilder.append(c); + } + } + } + + private void flushBuilder() { + if (mBuilder.length() > 0) { + Log.d(mTag, mBuilder.toString()); + mBuilder.delete(0, mBuilder.length()); + } + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/util/LruCache.java b/actionbarsherlock/library/src/android/support/v4/util/LruCache.java new file mode 100644 index 000000000..a7a2ee276 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/util/LruCache.java @@ -0,0 +1,323 @@ +/* + * 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 android.support.v4.util; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Static library version of {@code android.util.LruCache}. Used to write apps + * that run on API levels prior to 12. When running on API level 12 or above, + * this implementation is still used; it does not try to switch to the + * framework's implementation. See the framework SDK documentation for a class + * overview. + */ +public class LruCache { + private final LinkedHashMap map; + + /** Size of this cache in units. Not necessarily the number of elements. */ + private int size; + private int maxSize; + + private int putCount; + private int createCount; + private int evictionCount; + private int hitCount; + private int missCount; + + /** + * @param maxSize for caches that do not override {@link #sizeOf}, this is + * the maximum number of entries in the cache. For all other caches, + * this is the maximum sum of the sizes of the entries in this cache. + */ + public LruCache(int maxSize) { + if (maxSize <= 0) { + throw new IllegalArgumentException("maxSize <= 0"); + } + this.maxSize = maxSize; + this.map = new LinkedHashMap(0, 0.75f, true); + } + + /** + * Returns the value for {@code key} if it exists in the cache or can be + * created by {@code #create}. If a value was returned, it is moved to the + * head of the queue. This returns null if a value is not cached and cannot + * be created. + */ + public final V get(K key) { + if (key == null) { + throw new NullPointerException("key == null"); + } + + V mapValue; + synchronized (this) { + mapValue = map.get(key); + if (mapValue != null) { + hitCount++; + return mapValue; + } + missCount++; + } + + /* + * Attempt to create a value. This may take a long time, and the map + * may be different when create() returns. If a conflicting value was + * added to the map while create() was working, we leave that value in + * the map and release the created value. + */ + + V createdValue = create(key); + if (createdValue == null) { + return null; + } + + synchronized (this) { + createCount++; + mapValue = map.put(key, createdValue); + + if (mapValue != null) { + // There was a conflict so undo that last put + map.put(key, mapValue); + } else { + size += safeSizeOf(key, createdValue); + } + } + + if (mapValue != null) { + entryRemoved(false, key, createdValue, mapValue); + return mapValue; + } else { + trimToSize(maxSize); + return createdValue; + } + } + + /** + * Caches {@code value} for {@code key}. The value is moved to the head of + * the queue. + * + * @return the previous value mapped by {@code key}. + */ + public final V put(K key, V value) { + if (key == null || value == null) { + throw new NullPointerException("key == null || value == null"); + } + + V previous; + synchronized (this) { + putCount++; + size += safeSizeOf(key, value); + previous = map.put(key, value); + if (previous != null) { + size -= safeSizeOf(key, previous); + } + } + + if (previous != null) { + entryRemoved(false, key, previous, value); + } + + trimToSize(maxSize); + return previous; + } + + /** + * @param maxSize the maximum size of the cache before returning. May be -1 + * to evict even 0-sized elements. + */ + private void trimToSize(int maxSize) { + while (true) { + K key; + V value; + synchronized (this) { + if (size < 0 || (map.isEmpty() && size != 0)) { + throw new IllegalStateException(getClass().getName() + + ".sizeOf() is reporting inconsistent results!"); + } + + if (size <= maxSize || map.isEmpty()) { + break; + } + + Map.Entry toEvict = map.entrySet().iterator().next(); + key = toEvict.getKey(); + value = toEvict.getValue(); + map.remove(key); + size -= safeSizeOf(key, value); + evictionCount++; + } + + entryRemoved(true, key, value, null); + } + } + + /** + * Removes the entry for {@code key} if it exists. + * + * @return the previous value mapped by {@code key}. + */ + public final V remove(K key) { + if (key == null) { + throw new NullPointerException("key == null"); + } + + V previous; + synchronized (this) { + previous = map.remove(key); + if (previous != null) { + size -= safeSizeOf(key, previous); + } + } + + if (previous != null) { + entryRemoved(false, key, previous, null); + } + + return previous; + } + + /** + * Called for entries that have been evicted or removed. This method is + * invoked when a value is evicted to make space, removed by a call to + * {@link #remove}, or replaced by a call to {@link #put}. The default + * implementation does nothing. + * + *

The method is called without synchronization: other threads may + * access the cache while this method is executing. + * + * @param evicted true if the entry is being removed to make space, false + * if the removal was caused by a {@link #put} or {@link #remove}. + * @param newValue the new value for {@code key}, if it exists. If non-null, + * this removal was caused by a {@link #put}. Otherwise it was caused by + * an eviction or a {@link #remove}. + */ + protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {} + + /** + * Called after a cache miss to compute a value for the corresponding key. + * Returns the computed value or null if no value can be computed. The + * default implementation returns null. + * + *

The method is called without synchronization: other threads may + * access the cache while this method is executing. + * + *

If a value for {@code key} exists in the cache when this method + * returns, the created value will be released with {@link #entryRemoved} + * and discarded. This can occur when multiple threads request the same key + * at the same time (causing multiple values to be created), or when one + * thread calls {@link #put} while another is creating a value for the same + * key. + */ + protected V create(K key) { + return null; + } + + private int safeSizeOf(K key, V value) { + int result = sizeOf(key, value); + if (result < 0) { + throw new IllegalStateException("Negative size: " + key + "=" + value); + } + return result; + } + + /** + * Returns the size of the entry for {@code key} and {@code value} in + * user-defined units. The default implementation returns 1 so that size + * is the number of entries and max size is the maximum number of entries. + * + *

An entry's size must not change while it is in the cache. + */ + protected int sizeOf(K key, V value) { + return 1; + } + + /** + * Clear the cache, calling {@link #entryRemoved} on each removed entry. + */ + public final void evictAll() { + trimToSize(-1); // -1 will evict 0-sized elements + } + + /** + * For caches that do not override {@link #sizeOf}, this returns the number + * of entries in the cache. For all other caches, this returns the sum of + * the sizes of the entries in this cache. + */ + public synchronized final int size() { + return size; + } + + /** + * For caches that do not override {@link #sizeOf}, this returns the maximum + * number of entries in the cache. For all other caches, this returns the + * maximum sum of the sizes of the entries in this cache. + */ + public synchronized final int maxSize() { + return maxSize; + } + + /** + * Returns the number of times {@link #get} returned a value. + */ + public synchronized final int hitCount() { + return hitCount; + } + + /** + * Returns the number of times {@link #get} returned null or required a new + * value to be created. + */ + public synchronized final int missCount() { + return missCount; + } + + /** + * Returns the number of times {@link #create(Object)} returned a value. + */ + public synchronized final int createCount() { + return createCount; + } + + /** + * Returns the number of times {@link #put} was called. + */ + public synchronized final int putCount() { + return putCount; + } + + /** + * Returns the number of values that have been evicted. + */ + public synchronized final int evictionCount() { + return evictionCount; + } + + /** + * Returns a copy of the current contents of the cache, ordered from least + * recently accessed to most recently accessed. + */ + public synchronized final Map snapshot() { + return new LinkedHashMap(map); + } + + @Override public synchronized final String toString() { + int accesses = hitCount + missCount; + int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0; + return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]", + maxSize, hitCount, missCount, hitPercent); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/util/TimeUtils.java b/actionbarsherlock/library/src/android/support/v4/util/TimeUtils.java new file mode 100644 index 000000000..65ac8544e --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/util/TimeUtils.java @@ -0,0 +1,172 @@ +/* + * 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 android.support.v4.util; + +import java.io.PrintWriter; + +/** + * Useful time utilities that are not available on all versions of Android. + */ +public class TimeUtils { + /** @hide Field length that can hold 999 days of time */ + public static final int HUNDRED_DAY_FIELD_LEN = 19; + + private static final int SECONDS_PER_MINUTE = 60; + private static final int SECONDS_PER_HOUR = 60 * 60; + private static final int SECONDS_PER_DAY = 24 * 60 * 60; + + private static final Object sFormatSync = new Object(); + private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5]; + + static private int accumField(int amt, int suffix, boolean always, int zeropad) { + if (amt > 99 || (always && zeropad >= 3)) { + return 3+suffix; + } + if (amt > 9 || (always && zeropad >= 2)) { + return 2+suffix; + } + if (always || amt > 0) { + return 1+suffix; + } + return 0; + } + + static private int printField(char[] formatStr, int amt, char suffix, int pos, + boolean always, int zeropad) { + if (always || amt > 0) { + final int startPos = pos; + if ((always && zeropad >= 3) || amt > 99) { + int dig = amt/100; + formatStr[pos] = (char)(dig + '0'); + pos++; + amt -= (dig*100); + } + if ((always && zeropad >= 2) || amt > 9 || startPos != pos) { + int dig = amt/10; + formatStr[pos] = (char)(dig + '0'); + pos++; + amt -= (dig*10); + } + formatStr[pos] = (char)(amt + '0'); + pos++; + formatStr[pos] = suffix; + pos++; + } + return pos; + } + + private static int formatDurationLocked(long duration, int fieldLen) { + if (sFormatStr.length < fieldLen) { + sFormatStr = new char[fieldLen]; + } + + char[] formatStr = sFormatStr; + + if (duration == 0) { + int pos = 0; + fieldLen -= 1; + while (pos < fieldLen) { + formatStr[pos] = ' '; + } + formatStr[pos] = '0'; + return pos+1; + } + + char prefix; + if (duration > 0) { + prefix = '+'; + } else { + prefix = '-'; + duration = -duration; + } + + int millis = (int)(duration%1000); + int seconds = (int) Math.floor(duration / 1000); + int days = 0, hours = 0, minutes = 0; + + if (seconds > SECONDS_PER_DAY) { + days = seconds / SECONDS_PER_DAY; + seconds -= days * SECONDS_PER_DAY; + } + if (seconds > SECONDS_PER_HOUR) { + hours = seconds / SECONDS_PER_HOUR; + seconds -= hours * SECONDS_PER_HOUR; + } + if (seconds > SECONDS_PER_MINUTE) { + minutes = seconds / SECONDS_PER_MINUTE; + seconds -= minutes * SECONDS_PER_MINUTE; + } + + int pos = 0; + + if (fieldLen != 0) { + int myLen = accumField(days, 1, false, 0); + myLen += accumField(hours, 1, myLen > 0, 2); + myLen += accumField(minutes, 1, myLen > 0, 2); + myLen += accumField(seconds, 1, myLen > 0, 2); + myLen += accumField(millis, 2, true, myLen > 0 ? 3 : 0) + 1; + while (myLen < fieldLen) { + formatStr[pos] = ' '; + pos++; + myLen++; + } + } + + formatStr[pos] = prefix; + pos++; + + int start = pos; + boolean zeropad = fieldLen != 0; + pos = printField(formatStr, days, 'd', pos, false, 0); + pos = printField(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0); + pos = printField(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0); + pos = printField(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0); + pos = printField(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0); + formatStr[pos] = 's'; + return pos + 1; + } + + /** @hide Just for debugging; not internationalized. */ + public static void formatDuration(long duration, StringBuilder builder) { + synchronized (sFormatSync) { + int len = formatDurationLocked(duration, 0); + builder.append(sFormatStr, 0, len); + } + } + + /** @hide Just for debugging; not internationalized. */ + public static void formatDuration(long duration, PrintWriter pw, int fieldLen) { + synchronized (sFormatSync) { + int len = formatDurationLocked(duration, fieldLen); + pw.print(new String(sFormatStr, 0, len)); + } + } + + /** @hide Just for debugging; not internationalized. */ + public static void formatDuration(long duration, PrintWriter pw) { + formatDuration(duration, pw, 0); + } + + /** @hide Just for debugging; not internationalized. */ + public static void formatDuration(long time, long now, PrintWriter pw) { + if (time == 0) { + pw.print("--"); + return; + } + formatDuration(time-now, pw, 0); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/AccessibilityDelegateCompat.java b/actionbarsherlock/library/src/android/support/v4/view/AccessibilityDelegateCompat.java new file mode 100644 index 000000000..c70b94f8c --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/AccessibilityDelegateCompat.java @@ -0,0 +1,353 @@ +/* + * 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 android.support.v4.view; + +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; + +/** + * Helper for accessing AccessibilityDelegate from newer platform versions. + */ +public class AccessibilityDelegateCompat { + + static interface AccessibilityDelegateImpl { + public Object newAccessiblityDelegateDefaultImpl(); + public Object newAccessiblityDelegateBridge(AccessibilityDelegateCompat listener); + public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host, + AccessibilityEvent event); + public void onInitializeAccessibilityEvent(Object delegate, View host, + AccessibilityEvent event); + public void onInitializeAccessibilityNodeInfo(Object delegate, View host, Object info); + public void onPopulateAccessibilityEvent(Object delegate, View host, + AccessibilityEvent event); + public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child, + AccessibilityEvent event); + public void sendAccessibilityEvent(Object delegate, View host, int eventType); + public void sendAccessibilityEventUnchecked(Object delegate, View host, + AccessibilityEvent event); + } + + static class AccessibilityDelegateStubImpl implements AccessibilityDelegateImpl { + public Object newAccessiblityDelegateDefaultImpl() { + return null; + } + + public Object newAccessiblityDelegateBridge(AccessibilityDelegateCompat listener) { + return null; + } + + public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host, + AccessibilityEvent event) { + return false; + } + + public void onInitializeAccessibilityEvent(Object delegate, View host, + AccessibilityEvent event) { + + } + + public void onInitializeAccessibilityNodeInfo(Object delegate, View host, Object info) { + + } + + public void onPopulateAccessibilityEvent(Object delegate, View host, + AccessibilityEvent event) { + + } + + public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child, + AccessibilityEvent event) { + return true; + } + + public void sendAccessibilityEvent(Object delegate, View host, int eventType) { + + } + + public void sendAccessibilityEventUnchecked(Object delegate, View host, + AccessibilityEvent event) { + + } + } +/* + static class AccessibilityDelegateIcsImpl extends AccessibilityDelegateStubImpl { + @Override + public Object newAccessiblityDelegateDefaultImpl() { + return AccessibilityDelegateCompatIcs.newAccessibilityDelegateDefaultImpl(); + } + + @Override + public Object newAccessiblityDelegateBridge(final AccessibilityDelegateCompat compat) { + return AccessibilityDelegateCompatIcs.newAccessibilityDelegateBridge( + new AccessibilityDelegateCompatIcs.AccessibilityDelegateBridge() { + @Override + public boolean dispatchPopulateAccessibilityEvent(View host, + AccessibilityEvent event) { + return compat.dispatchPopulateAccessibilityEvent(host, event); + } + + @Override + public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { + compat.onInitializeAccessibilityEvent(host, event); + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, Object info) { + compat.onInitializeAccessibilityNodeInfo(host, + new AccessibilityNodeInfoCompat(info)); + } + + @Override + public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { + compat.onPopulateAccessibilityEvent(host, event); + } + + @Override + public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, + AccessibilityEvent event) { + return compat.onRequestSendAccessibilityEvent(host, child, event); + } + + @Override + public void sendAccessibilityEvent(View host, int eventType) { + compat.sendAccessibilityEvent(host, eventType); + } + + @Override + public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { + compat.sendAccessibilityEventUnchecked(host, event); + } + }); + } + + @Override + public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host, + AccessibilityEvent event) { + return AccessibilityDelegateCompatIcs.dispatchPopulateAccessibilityEvent(delegate, + host, event); + } + + @Override + public void onInitializeAccessibilityEvent(Object delegate, View host, + AccessibilityEvent event) { + AccessibilityDelegateCompatIcs.onInitializeAccessibilityEvent(delegate, host, event); + } + + @Override + public void onInitializeAccessibilityNodeInfo(Object delegate, View host, Object info) { + AccessibilityDelegateCompatIcs.onInitializeAccessibilityNodeInfo(delegate, host, info); + } + + @Override + public void onPopulateAccessibilityEvent(Object delegate, View host, + AccessibilityEvent event) { + AccessibilityDelegateCompatIcs.onPopulateAccessibilityEvent(delegate, host, event); + } + + @Override + public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child, + AccessibilityEvent event) { + return AccessibilityDelegateCompatIcs.onRequestSendAccessibilityEvent(delegate, host, + child, event); + } + + @Override + public void sendAccessibilityEvent(Object delegate, View host, int eventType) { + AccessibilityDelegateCompatIcs.sendAccessibilityEvent(delegate, host, eventType); + } + + @Override + public void sendAccessibilityEventUnchecked(Object delegate, View host, + AccessibilityEvent event) { + AccessibilityDelegateCompatIcs.sendAccessibilityEventUnchecked(delegate, host, event); + } + } +*/ + private static final AccessibilityDelegateImpl IMPL; + private static final Object DEFAULT_DELEGATE; + + static { + //if (Build.VERSION.SDK_INT >= 14) { // ICS + // IMPL = new AccessibilityDelegateIcsImpl(); + //} else { + IMPL = new AccessibilityDelegateStubImpl(); + //} + DEFAULT_DELEGATE = IMPL.newAccessiblityDelegateDefaultImpl(); + } + + final Object mBridge; + + /** + * Creates a new instance. + */ + public AccessibilityDelegateCompat() { + mBridge = IMPL.newAccessiblityDelegateBridge(this); + } + + /** + * @return The wrapped bridge implementation. + */ + Object getBridge() { + return mBridge; + } + + /** + * Sends an accessibility event of the given type. If accessibility is not + * enabled this method has no effect. + *

+ * The default implementation behaves as {@link View#sendAccessibilityEvent(int) + * View#sendAccessibilityEvent(int)} for the case of no accessibility delegate + * been set. + *

+ * + * @param host The View hosting the delegate. + * @param eventType The type of the event to send. + * + * @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int) + */ + public void sendAccessibilityEvent(View host, int eventType) { + IMPL.sendAccessibilityEvent(DEFAULT_DELEGATE, host, eventType); + } + + /** + * Sends an accessibility event. This method behaves exactly as + * {@link #sendAccessibilityEvent(View, int)} but takes as an argument an + * empty {@link AccessibilityEvent} and does not perform a check whether + * accessibility is enabled. + *

+ * The default implementation behaves as + * {@link View#sendAccessibilityEventUnchecked(AccessibilityEvent) + * View#sendAccessibilityEventUnchecked(AccessibilityEvent)} for + * the case of no accessibility delegate been set. + *

+ * + * @param host The View hosting the delegate. + * @param event The event to send. + * + * @see View#sendAccessibilityEventUnchecked(AccessibilityEvent) + * View#sendAccessibilityEventUnchecked(AccessibilityEvent) + */ + public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { + IMPL.sendAccessibilityEventUnchecked(DEFAULT_DELEGATE, host, event); + } + + /** + * Dispatches an {@link AccessibilityEvent} to the host {@link View} first and then + * to its children for adding their text content to the event. + *

+ * The default implementation behaves as + * {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) + * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)} for + * the case of no accessibility delegate been set. + *

+ * + * @param host The View hosting the delegate. + * @param event The event. + * @return True if the event population was completed. + * + * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) + * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) + */ + public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) { + return IMPL.dispatchPopulateAccessibilityEvent(DEFAULT_DELEGATE, host, event); + } + + /** + * Gives a chance to the host View to populate the accessibility event with its + * text content. + *

+ * The default implementation behaves as + * {@link ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent) + * ViewCompat#onPopulateAccessibilityEvent(AccessibilityEvent)} for + * the case of no accessibility delegate been set. + *

+ * + * @param host The View hosting the delegate. + * @param event The accessibility event which to populate. + * + * @see ViewCompat#onPopulateAccessibilityEvent(View ,AccessibilityEvent) + * ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent) + */ + public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { + IMPL.onPopulateAccessibilityEvent(DEFAULT_DELEGATE, host, event); + } + + /** + * Initializes an {@link AccessibilityEvent} with information about the + * the host View which is the event source. + *

+ * The default implementation behaves as + * {@link ViewCompat#onInitializeAccessibilityEvent(View v, AccessibilityEvent event) + * ViewCompat#onInitalizeAccessibilityEvent(View v, AccessibilityEvent event)} for + * the case of no accessibility delegate been set. + *

+ * + * @param host The View hosting the delegate. + * @param event The event to initialize. + * + * @see ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent) + * ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent) + */ + public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { + IMPL.onInitializeAccessibilityEvent(DEFAULT_DELEGATE, host, event); + } + + /** + * Initializes an {@link AccessibilityNodeInfoCompat} with information about the host view. + *

+ * The default implementation behaves as + * {@link ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat) + * ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)} for + * the case of no accessibility delegate been set. + *

+ * + * @param host The View hosting the delegate. + * @param info The instance to initialize. + * + * @see ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat) + * ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat) + */ + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { + IMPL.onInitializeAccessibilityNodeInfo(DEFAULT_DELEGATE, host, info); + } + + /** + * Called when a child of the host View has requested sending an + * {@link AccessibilityEvent} and gives an opportunity to the parent (the host) + * to augment the event. + *

+ * The default implementation behaves as + * {@link ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent) + * ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)} for + * the case of no accessibility delegate been set. + *

+ * + * @param host The View hosting the delegate. + * @param child The child which requests sending the event. + * @param event The event to be sent. + * @return True if the event should be sent + * + * @see ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent) + * ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent) + */ + public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, + AccessibilityEvent event) { + return IMPL.onRequestSendAccessibilityEvent(DEFAULT_DELEGATE, host, child, event); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/AccessibilityDelegateCompatIcs.java b/actionbarsherlock/library/src/android/support/v4/view/AccessibilityDelegateCompatIcs.java new file mode 100644 index 000000000..ed0c7e84d --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/AccessibilityDelegateCompatIcs.java @@ -0,0 +1,120 @@ +/* + * 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 android.support.v4.view; + +//import android.view.View; +//import android.view.View.AccessibilityDelegate; +//import android.view.ViewGroup; +//import android.view.accessibility.AccessibilityEvent; +//import android.view.accessibility.AccessibilityNodeInfo; + +/** + * ICS specific AccessibilityDelegate API implementation. + */ +class AccessibilityDelegateCompatIcs { +/* + public interface AccessibilityDelegateBridge { + public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event); + public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event); + public void onInitializeAccessibilityNodeInfo(View host, Object info); + public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event); + public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, + AccessibilityEvent event); + public void sendAccessibilityEvent(View host, int eventType); + public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event); + } + + public static Object newAccessibilityDelegateDefaultImpl() { + return new AccessibilityDelegate(); + } + + public static Object newAccessibilityDelegateBridge(final AccessibilityDelegateBridge bridge) { + return new AccessibilityDelegate() { + @Override + public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) { + return bridge.dispatchPopulateAccessibilityEvent(host, event); + } + + @Override + public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { + bridge.onInitializeAccessibilityEvent(host, event); + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + bridge.onInitializeAccessibilityNodeInfo(host, info); + } + + @Override + public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { + bridge.onPopulateAccessibilityEvent(host, event); + } + + @Override + public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, + AccessibilityEvent event) { + return bridge.onRequestSendAccessibilityEvent(host, child, event); + } + + @Override + public void sendAccessibilityEvent(View host, int eventType) { + bridge.sendAccessibilityEvent(host, eventType); + } + + @Override + public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { + bridge.sendAccessibilityEventUnchecked(host, event); + } + }; + } + + public static boolean dispatchPopulateAccessibilityEvent(Object delegate, View host, + AccessibilityEvent event) { + return ((AccessibilityDelegate) delegate).dispatchPopulateAccessibilityEvent(host, event); + } + + public static void onInitializeAccessibilityEvent(Object delegate, View host, + AccessibilityEvent event) { + ((AccessibilityDelegate) delegate).onInitializeAccessibilityEvent(host, event); + } + + public static void onInitializeAccessibilityNodeInfo(Object delegate, View host, Object info) { + ((AccessibilityDelegate) delegate).onInitializeAccessibilityNodeInfo(host, + (AccessibilityNodeInfo) info); + } + + public static void onPopulateAccessibilityEvent(Object delegate, View host, + AccessibilityEvent event) { + ((AccessibilityDelegate) delegate).onPopulateAccessibilityEvent(host, event); + } + + public static boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, + View child, AccessibilityEvent event) { + return ((AccessibilityDelegate) delegate).onRequestSendAccessibilityEvent(host, child, + event); + } + + public static void sendAccessibilityEvent(Object delegate, View host, int eventType) { + ((AccessibilityDelegate) delegate).sendAccessibilityEvent(host, eventType); + } + + public static void sendAccessibilityEventUnchecked(Object delegate, View host, + AccessibilityEvent event) { + ((AccessibilityDelegate) delegate).sendAccessibilityEventUnchecked(host, event); + } +*/ +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/ActionMode.java b/actionbarsherlock/library/src/android/support/v4/view/ActionMode.java new file mode 100644 index 000000000..c84dedb19 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/ActionMode.java @@ -0,0 +1,188 @@ +/* + * Copyright 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 android.support.v4.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 { + /** + *

Callback interface for action modes. Supplied to + * {@link android.support.v4.app.FragmentActivity#startActionMode(Callback)}, + * a Callback configures and handles events raised by a user's interaction + * with an action mode.

+ * + *

An action mode's lifecycle is as follows: + *

    + *
  • {@link #onCreateActionMode(ActionMode, Menu)} once on initial + * creation
  • + *
  • {@link #onPrepareActionMode(ActionMode, Menu)} after creation and + * any time the ActionMode is invalidated
  • + *
  • {@link #onActionItemClicked(ActionMode, MenuItem)} any time a + * contextual action button is clicked
  • + *
  • {@link #onDestroyActionMode(ActionMode)} when the action mode is + * closed
  • + *
+ *

+ */ + public interface Callback { + /** + * 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. + */ + boolean onActionItemClicked(ActionMode mode, MenuItem item); + + /** + * 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. + */ + boolean onCreateActionMode(ActionMode mode, Menu menu); + + /** + * Called when an action mode is about to be exited and destroyed. + * + * @param mode The current ActionMode being destroyed + */ + void onDestroyActionMode(ActionMode mode); + + /** + * 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. + */ + boolean onPrepareActionMode(ActionMode mode, Menu menu); + } + + /** + * Finish and close this action mode. The action mode's + * {@link ActionMode.Callback} will have its + * {@link ActionMode.Callback#onDestroyActionMode(ActionMode)} method + * called. + */ + public abstract void finish(); + + /** + * Returns the current custom view for this action mode. + * + * @return The current custom view + */ + public abstract View getCustomView(); + + /** + * Returns the menu of actions that this action mode presents. + * + * @return The action mode's menu. + */ + public abstract Menu getMenu(); + + /** + * Returns a {@link MenuInflater} with the ActionMode's context. + * + * @return Menu inflater. + */ + public abstract MenuInflater getMenuInflater(); + + /** + * Returns the current subtitle of this action mode. + * + * @return Subtitle text + */ + public abstract CharSequence getSubtitle(); + + /** + * Returns the current title of this action mode. + * + * @return Title text + */ + public abstract CharSequence getTitle(); + + /** + * Invalidate the action mode and refresh menu content. The mode's + * {@link ActionMode.Callback} will have its + * {@link ActionMode.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(); + + /** + * 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); + + /** + * 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 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 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 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); +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/KeyEventCompat.java b/actionbarsherlock/library/src/android/support/v4/view/KeyEventCompat.java new file mode 100644 index 000000000..0967592d6 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/KeyEventCompat.java @@ -0,0 +1,144 @@ +/* + * 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 android.support.v4.view; + +import android.view.KeyEvent; + +/** + * Helper for accessing newer features in KeyEvent. + */ +public class KeyEventCompat { + /** + * Interface for the full API. + */ + interface KeyEventVersionImpl { + public int normalizeMetaState(int metaState); + public boolean metaStateHasModifiers(int metaState, int modifiers); + public boolean metaStateHasNoModifiers(int metaState); + } + + /** + * Interface implementation that doesn't use anything about v4 APIs. + */ + static class BaseKeyEventVersionImpl implements KeyEventVersionImpl { + private static final int META_MODIFIER_MASK = + KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON + | KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON + | KeyEvent.META_SYM_ON; + + // Mask of all lock key meta states. + private static final int META_ALL_MASK = META_MODIFIER_MASK; + + private static int metaStateFilterDirectionalModifiers(int metaState, + int modifiers, int basic, int left, int right) { + final boolean wantBasic = (modifiers & basic) != 0; + final int directional = left | right; + final boolean wantLeftOrRight = (modifiers & directional) != 0; + + if (wantBasic) { + if (wantLeftOrRight) { + throw new IllegalArgumentException("bad arguments"); + } + return metaState & ~directional; + } else if (wantLeftOrRight) { + return metaState & ~basic; + } else { + return metaState; + } + } + + @Override + public int normalizeMetaState(int metaState) { + if ((metaState & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON)) != 0) { + metaState |= KeyEvent.META_SHIFT_ON; + } + if ((metaState & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON)) != 0) { + metaState |= KeyEvent.META_ALT_ON; + } + return metaState & META_ALL_MASK; + } + + @Override + public boolean metaStateHasModifiers(int metaState, int modifiers) { + metaState = normalizeMetaState(metaState) & META_MODIFIER_MASK; + metaState = metaStateFilterDirectionalModifiers(metaState, modifiers, + KeyEvent.META_SHIFT_ON, KeyEvent.META_SHIFT_LEFT_ON, KeyEvent.META_SHIFT_RIGHT_ON); + metaState = metaStateFilterDirectionalModifiers(metaState, modifiers, + KeyEvent.META_ALT_ON, KeyEvent.META_ALT_LEFT_ON, KeyEvent.META_ALT_RIGHT_ON); + return metaState == modifiers; + } + + @Override + public boolean metaStateHasNoModifiers(int metaState) { + return (normalizeMetaState(metaState) & META_MODIFIER_MASK) == 0; + } + } + + /** + * Interface implementation for devices with at least v11 APIs. + */ + static class HoneycombKeyEventVersionImpl implements KeyEventVersionImpl { + @Override + public int normalizeMetaState(int metaState) { + return KeyEventCompatHoneycomb.normalizeMetaState(metaState); + } + + @Override + public boolean metaStateHasModifiers(int metaState, int modifiers) { + return KeyEventCompatHoneycomb.metaStateHasModifiers(metaState, modifiers); + } + + @Override + public boolean metaStateHasNoModifiers(int metaState) { + return KeyEventCompatHoneycomb.metaStateHasNoModifiers(metaState); + } + } + + /** + * Select the correct implementation to use for the current platform. + */ + static final KeyEventVersionImpl IMPL; + static { + if (android.os.Build.VERSION.SDK_INT >= 11) { + IMPL = new HoneycombKeyEventVersionImpl(); + } else { + IMPL = new BaseKeyEventVersionImpl(); + } + } + + // ------------------------------------------------------------------- + + public static int normalizeMetaState(int metaState) { + return IMPL.normalizeMetaState(metaState); + } + + public static boolean metaStateHasModifiers(int metaState, int modifiers) { + return IMPL.metaStateHasModifiers(metaState, modifiers); + } + + public static boolean metaStateHasNoModifiers(int metaState) { + return IMPL.metaStateHasNoModifiers(metaState); + } + + public static boolean hasModifiers(KeyEvent event, int modifiers) { + return IMPL.metaStateHasModifiers(event.getMetaState(), modifiers); + } + + public static boolean hasNoModifiers(KeyEvent event) { + return IMPL.metaStateHasNoModifiers(event.getMetaState()); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/KeyEventCompatHoneycomb.java b/actionbarsherlock/library/src/android/support/v4/view/KeyEventCompatHoneycomb.java new file mode 100644 index 000000000..8819357ff --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/KeyEventCompatHoneycomb.java @@ -0,0 +1,36 @@ +/* + * 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 android.support.v4.view; + +import android.view.KeyEvent; + +/** + * Implementation of key event compatibility that can call Honeycomb APIs. + */ +class KeyEventCompatHoneycomb { + public static int normalizeMetaState(int metaState) { + return KeyEvent.normalizeMetaState(metaState); + } + + public static boolean metaStateHasModifiers(int metaState, int modifiers) { + return KeyEvent.metaStateHasModifiers(metaState, modifiers); + } + + public static boolean metaStateHasNoModifiers(int metaState) { + return KeyEvent.metaStateHasNoModifiers(metaState); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/Menu.java b/actionbarsherlock/library/src/android/support/v4/view/Menu.java new file mode 100644 index 000000000..39a697181 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/Menu.java @@ -0,0 +1,33 @@ +package android.support.v4.view; + +public interface Menu extends android.view.Menu { + @Override + MenuItem add(CharSequence title); + + @Override + MenuItem add(int groupId, int itemId, int order, int titleRes); + + @Override + MenuItem add(int titleRes); + + @Override + MenuItem add(int groupId, int itemId, int order, CharSequence title); + + @Override + SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title); + + @Override + SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes); + + @Override + SubMenu addSubMenu(CharSequence title); + + @Override + SubMenu addSubMenu(int titleRes); + + @Override + MenuItem findItem(int id); + + @Override + MenuItem getItem(int index); +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/MenuInflater.java b/actionbarsherlock/library/src/android/support/v4/view/MenuInflater.java new file mode 100644 index 000000000..4d2a0a2b0 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/MenuInflater.java @@ -0,0 +1,463 @@ +/* + * 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 android.support.v4.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.XmlResourceParser; +import android.util.AttributeSet; +import android.util.Xml; +import android.view.InflateException; +import android.view.View; +import com.actionbarsherlock.internal.view.menu.MenuBuilder; +import com.actionbarsherlock.internal.view.menu.MenuItemImpl; +import com.actionbarsherlock.internal.view.menu.SubMenuBuilder; + +/** + * This class is used to instantiate menu XML files into Menu objects. + *

+ * 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. + * something file.) + */ +public final class MenuInflater extends android.view.MenuInflater { + private static final Class[] ACTION_VIEW_CONSTRUCTOR_SIGNATURE = new Class[] { Context.class }; + private static final Class[] PARAM_TYPES = new Class[] { android.support.v4.view.MenuItem.class }; + + /** Android XML namespace. */ + private static final String XML_NS = "http://schemas.android.com/apk/res/android"; + + /** 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"; + + + /** Context from which to inflate resources. */ + private final Context mContext; + + /** Native inflater for context menu fallback. */ + private final android.view.MenuInflater mNativeMenuInflater; + + + /** + * Constructs a menu inflater. + * + * @see Activity#getMenuInflater() + */ + public MenuInflater(Context context, android.view.MenuInflater nativeMenuInflater) { + super(context); + mContext = context; + mNativeMenuInflater = nativeMenuInflater; + } + + + /** + * 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., + * R.menu.main_activity) + * @param menu The Menu to inflate into. The items and submenus will be + * added to this Menu. + */ + @Override + public void inflate(int menuRes, android.view.Menu menu) { + if (!(menu instanceof MenuBuilder)) { + mNativeMenuInflater.inflate(menuRes, menu); + return; + } + + MenuBuilder actionBarMenu = (MenuBuilder)menu; + XmlResourceParser parser = null; + try { + parser = mContext.getResources().getLayout(menuRes); + AttributeSet attrs = Xml.asAttributeSet(parser); + + parseMenu(parser, attrs, actionBarMenu); + } 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, MenuBuilder menu) + throws XmlPullParserException, IOException { + ActionBarMenuState menuState = new ActionBarMenuState(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 + SubMenuBuilder 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()) { + 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(); + } + } + + + + /** + * State for the current menu. + *

+ * Groups can not be nested unless there is another menu (which will have + * its state class). + */ + private final class ActionBarMenuState { + private final MenuBuilder 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 String itemTitle; + private String 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; + private String itemListenerMethodName; + private int itemShowAsAction; + private int itemActionLayout; + private String itemActionViewClassName; + + private static final int defaultGroupId = View.NO_ID; + private static final int defaultItemId = View.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; + private static final int defaultItemShowAsAction = 0; + private static final int defaultIconResId = View.NO_ID; + + /** Mirror of package-scoped Menu.CATEGORY_MASK. */ + private static final int Menu__CATEGORY_MASK = 0xffff0000; + /** Mirror of package-scoped Menu.USER_MASK. */ + private static final int Menu__USER_MASK = 0x0000ffff; + + public ActionBarMenuState(MenuBuilder 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, com.android.internal.R.styleable.MenuGroup); + + //groupId = a.getResourceId(com.android.internal.R.styleable.MenuGroup_id, defaultGroupId); + groupId = attrs.getAttributeResourceValue(XML_NS, "id", defaultGroupId); + + //groupCategory = a.getInt(com.android.internal.R.styleable.MenuGroup_menuCategory, defaultItemCategory); + groupCategory = attrs.getAttributeIntValue(XML_NS, "menuCategory", defaultItemCategory); + + //groupOrder = a.getInt(com.android.internal.R.styleable.MenuGroup_orderInCategory, defaultItemOrder); + groupOrder = attrs.getAttributeIntValue(XML_NS, "orderInCategory", defaultItemOrder); + + //groupCheckable = a.getInt(com.android.internal.R.styleable.MenuGroup_checkableBehavior, defaultItemCheckable); + groupCheckable = attrs.getAttributeIntValue(XML_NS, "checkableBehavior", defaultItemCheckable); + + //groupVisible = a.getBoolean(com.android.internal.R.styleable.MenuGroup_visible, defaultItemVisible); + groupVisible = attrs.getAttributeBooleanValue(XML_NS, "visible", defaultItemVisible); + + //groupEnabled = a.getBoolean(com.android.internal.R.styleable.MenuGroup_enabled, defaultItemEnabled); + groupEnabled = attrs.getAttributeBooleanValue(XML_NS, "enabled", defaultItemEnabled); + + //a.recycle(); + } + + /** + * Called when the parser is pointing to an item tag. + */ + public void readItem(AttributeSet attrs) { + //TypedArray a = mContext.obtainStyledAttributes(attrs, com.android.internal.R.styleable.MenuItem); + + // Inherit attributes from the group as default value + + //itemId = a.getResourceId(com.android.internal.R.styleable.MenuItem_id, defaultItemId); + itemId = attrs.getAttributeResourceValue(XML_NS, "id", defaultItemId); + + //final int category = a.getInt(com.android.internal.R.styleable.MenuItem_menuCategory, groupCategory); + final int category = attrs.getAttributeIntValue(XML_NS, "menuCategory", groupCategory); + + //final int order = a.getInt(com.android.internal.R.styleable.MenuItem_orderInCategory, groupOrder); + final int order = attrs.getAttributeIntValue(XML_NS, "orderInCategory", groupOrder); + + //itemCategoryOrder = (category & Menu.CATEGORY_MASK) | (order & Menu.USER_MASK); + itemCategoryOrder = (category & Menu__CATEGORY_MASK) | (order & Menu__USER_MASK); + + //itemTitle = a.getString(com.android.internal.R.styleable.MenuItem_title); + final int itemTitleId = attrs.getAttributeResourceValue(XML_NS, "title", 0); + if (itemTitleId != 0) { + itemTitle = mContext.getString(itemTitleId); + } else { + itemTitle = attrs.getAttributeValue(XML_NS, "title"); + } + + //itemTitleCondensed = a.getString(com.android.internal.R.styleable.MenuItem_titleCondensed); + final int itemTitleCondensedId = attrs.getAttributeResourceValue(XML_NS, "titleCondensed", 0); + if (itemTitleCondensedId != 0) { + itemTitleCondensed = mContext.getString(itemTitleCondensedId); + } else { + itemTitleCondensed = attrs.getAttributeValue(XML_NS, "titleCondensed"); + } + + //itemIconResId = a.getResourceId(com.android.internal.R.styleable.MenuItem_icon, 0); + itemIconResId = attrs.getAttributeResourceValue(XML_NS, "icon", defaultIconResId); + + //itemAlphabeticShortcut = getShortcut(a.getString(com.android.internal.R.styleable.MenuItem_alphabeticShortcut)); + itemAlphabeticShortcut = getShortcut(attrs.getAttributeValue(XML_NS, "alphabeticShortcut")); + + //itemNumericShortcut = getShortcut(a.getString(com.android.internal.R.styleable.MenuItem_numericShortcut)); + itemNumericShortcut = getShortcut(attrs.getAttributeValue(XML_NS, "numericShortcut")); + + //if (a.hasValue(com.android.internal.R.styleable.MenuItem_checkable)) { + if (attrs.getAttributeValue(XML_NS, "checkable") != null) { + // Item has attribute checkable, use it + //itemCheckable = a.getBoolean(com.android.internal.R.styleable.MenuItem_checkable, false) ? 1 : 0; + itemCheckable = attrs.getAttributeBooleanValue(XML_NS, "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(com.android.internal.R.styleable.MenuItem_checked, defaultItemChecked); + itemChecked = attrs.getAttributeBooleanValue(XML_NS, "checked", defaultItemChecked); + + //itemVisible = a.getBoolean(com.android.internal.R.styleable.MenuItem_visible, groupVisible); + itemVisible = attrs.getAttributeBooleanValue(XML_NS, "visible", groupVisible); + + //itemEnabled = a.getBoolean(com.android.internal.R.styleable.MenuItem_enabled, groupEnabled); + itemEnabled = attrs.getAttributeBooleanValue(XML_NS, "enabled", groupEnabled); + + //presumed emulation of 3.0+'s MenuInflator: + itemListenerMethodName = attrs.getAttributeValue(XML_NS, "onClick"); + itemShowAsAction = attrs.getAttributeIntValue(XML_NS, "showAsAction", defaultItemShowAsAction); + itemActionLayout = attrs.getAttributeResourceValue(XML_NS, "actionLayout", 0); + itemActionViewClassName = attrs.getAttributeValue(XML_NS, "actionViewClass"); + + //a.recycle(); + + itemAdded = false; + } + + private char getShortcut(String shortcutString) { + if (shortcutString == null) { + return 0; + } else { + return shortcutString.charAt(0); + } + } + + private void setItem(MenuItemImpl 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 (MenuInflater.this.mContext.isRestricted()) { + throw new IllegalStateException("The android:onClick attribute cannot be used within a restricted context"); + } + item.setOnMenuItemClickListener(new InflatedOnMenuItemClickListener(itemListenerMethodName)); + } + if (itemCheckable >= 2) { + item.setExclusiveCheckable(true); + } + if (itemActionViewClassName != null) { + try { + Context context = MenuInflater.this.mContext; + ClassLoader loader = context.getClassLoader(); + Class actionViewClass = Class.forName(itemActionViewClassName, true, loader); + Constructor constructor = actionViewClass.getConstructor(ACTION_VIEW_CONSTRUCTOR_SIGNATURE); + View actionView = (View)constructor.newInstance(new Object[] { context }); + item.setActionView(actionView); + } catch (Exception e) { + throw new InflateException(e); + } + } else if (itemActionLayout > 0) { + item.setActionView(itemActionLayout); + } + } + + public void addItem() { + itemAdded = true; + setItem((MenuItemImpl)menu.add(groupId, itemId, itemCategoryOrder, itemTitle)); + } + + public SubMenuBuilder addSubMenuItem() { + itemAdded = true; + SubMenuBuilder subMenu = menu.addSubMenu(groupId, itemId, itemCategoryOrder, itemTitle); + setItem(subMenu.getItem()); + return subMenu; + } + + public boolean hasAddedItem() { + return itemAdded; + } + } + + class InflatedOnMenuItemClickListener extends android.support.v4.view.MenuItem.OnMenuItemClickListener { + private Method mMethod; + + public InflatedOnMenuItemClickListener(String methodName) { + final Class localClass = mContext.getClass(); + try { + mMethod = localClass.getMethod(methodName, PARAM_TYPES); + } catch (Exception e) { + StringBuilder b = new StringBuilder(); + b.append("Couldn't resolve menu item onClick handler "); + b.append(methodName); + b.append(" in class "); + b.append(localClass.getName()); + throw new InflateException(b.toString(), e); + } + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + final Object[] params = new Object[] { item }; + try { + if (mMethod.getReturnType() == Boolean.TYPE) { + return (Boolean)mMethod.invoke(mContext, params); + } + return false; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } +} \ No newline at end of file diff --git a/actionbarsherlock/library/src/android/support/v4/view/MenuItem.java b/actionbarsherlock/library/src/android/support/v4/view/MenuItem.java new file mode 100644 index 000000000..26ddd7973 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/MenuItem.java @@ -0,0 +1,187 @@ +/* + * 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 android.support.v4.view; + +import com.actionbarsherlock.internal.view.menu.MenuItemWrapper; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.view.View; + +/** + *

Interface for direct access to a previously created menu item.

+ * + *

An Item is returned by calling one of the {@link Menu#add(int)} + * methods.

+ * + *

For a feature set of specific menu types, see {@link Menu}.

+ */ +public interface MenuItem extends android.view.MenuItem { + /** + * Interface definition for a callback to be invoked when a menu item is + * clicked. + */ + public static abstract class OnMenuItemClickListener implements android.view.MenuItem.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 abstract boolean onMenuItemClick(MenuItem item); + + @Override + public final boolean onMenuItemClick(android.view.MenuItem item) { + return this.onMenuItemClick(new MenuItemWrapper(item)); + } + } + + + + /** + * 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; + + /** + * 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; + + /** + * 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; + + /** + * 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; + + + + /** + * Returns the currently set action view for this menu item. + * + * @return The item's action view + * @see #setActionView(int) + * @see #setActionView(View) + * @see #setShowAsAction(int) + */ + View getActionView(); + + /** + * 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. + * + * @param resId Layout resource to use for presenting this item to the user. + * @return This Item so additional setters can be called. + * @see #setActionView(View) + */ + MenuItem setActionView(int resId); + + /** + * 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. + * + * @param view View to use for presenting this item to the user. + * @return This Item so additional setters can be called. + * @see #setActionView(int) + */ + MenuItem setActionView(View view); + + /** + * 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}. + * {@link #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}. {@link #SHOW_AS_ACTION_NEVER} is the + * default. + */ + void setShowAsAction(int actionEnum); + + // --------------------------------------------------------------------- + // MENU ITEM SUPPORT + // --------------------------------------------------------------------- + + @Override + SubMenu getSubMenu(); + + @Override + MenuItem setAlphabeticShortcut(char alphaChar); + + @Override + MenuItem setCheckable(boolean checkable); + + @Override + MenuItem setChecked(boolean checked); + + @Override + MenuItem setEnabled(boolean enabled); + + @Override + MenuItem setIcon(Drawable icon); + + @Override + MenuItem setIcon(int iconRes); + + @Override + MenuItem setIntent(Intent intent); + + @Override + MenuItem setNumericShortcut(char numericChar); + + /** + * Set a custom listener for invocation of this menu item. + * + * @param menuItemClickListener The object to receive invokations. + * @return This Item so additional setters can be called. + */ + MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener); + + @Override + MenuItem setShortcut(char numericChar, char alphaChar); + + @Override + MenuItem setTitle(CharSequence title); + + @Override + MenuItem setTitle(int title); + + @Override + MenuItem setTitleCondensed(CharSequence title); + + @Override + MenuItem setVisible(boolean visible); +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/MotionEventCompat.java b/actionbarsherlock/library/src/android/support/v4/view/MotionEventCompat.java new file mode 100644 index 000000000..e88ab63c7 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/MotionEventCompat.java @@ -0,0 +1,194 @@ +/* + * 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 android.support.v4.view; + +import android.view.MotionEvent; + +/** + * Helper for accessing newer features in MotionEvent. + */ +public class MotionEventCompat { + /** + * Interface for the full API. + */ + interface MotionEventVersionImpl { + public int findPointerIndex(MotionEvent event, int pointerId); + public int getPointerId(MotionEvent event, int pointerIndex); + public float getX(MotionEvent event, int pointerIndex); + public float getY(MotionEvent event, int pointerIndex); + } + + /** + * Interface implementation that doesn't use anything about v4 APIs. + */ + static class BaseMotionEventVersionImpl implements MotionEventVersionImpl { + @Override + public int findPointerIndex(MotionEvent event, int pointerId) { + if (pointerId == 0) { + // id 0 == index 0 and vice versa. + return 0; + } + return -1; + } + @Override + public int getPointerId(MotionEvent event, int pointerIndex) { + if (pointerIndex == 0) { + // index 0 == id 0 and vice versa. + return 0; + } + throw new IndexOutOfBoundsException("Pre-Eclair does not support multiple pointers"); + } + @Override + public float getX(MotionEvent event, int pointerIndex) { + if (pointerIndex == 0) { + return event.getX(); + } + throw new IndexOutOfBoundsException("Pre-Eclair does not support multiple pointers"); + } + @Override + public float getY(MotionEvent event, int pointerIndex) { + if (pointerIndex == 0) { + return event.getY(); + } + throw new IndexOutOfBoundsException("Pre-Eclair does not support multiple pointers"); + } + } + + /** + * Interface implementation for devices with at least v11 APIs. + */ + static class EclairMotionEventVersionImpl implements MotionEventVersionImpl { + @Override + public int findPointerIndex(MotionEvent event, int pointerId) { + return MotionEventCompatEclair.findPointerIndex(event, pointerId); + } + @Override + public int getPointerId(MotionEvent event, int pointerIndex) { + return MotionEventCompatEclair.getPointerId(event, pointerIndex); + } + @Override + public float getX(MotionEvent event, int pointerIndex) { + return MotionEventCompatEclair.getX(event, pointerIndex); + } + @Override + public float getY(MotionEvent event, int pointerIndex) { + return MotionEventCompatEclair.getY(event, pointerIndex); + } + } + + /** + * Select the correct implementation to use for the current platform. + */ + static final MotionEventVersionImpl IMPL; + static { + if (android.os.Build.VERSION.SDK_INT >= 5) { + IMPL = new EclairMotionEventVersionImpl(); + } else { + IMPL = new BaseMotionEventVersionImpl(); + } + } + + // ------------------------------------------------------------------- + + /** + * Synonym for {@link MotionEvent#ACTION_MASK}. + */ + public static final int ACTION_MASK = 0xff; + + /** + * Synonym for {@link MotionEvent#ACTION_POINTER_DOWN}. + */ + public static final int ACTION_POINTER_DOWN = 5; + + /** + * Synonym for {@link MotionEvent#ACTION_POINTER_UP}. + */ + public static final int ACTION_POINTER_UP = 6; + + /** + * Synonym for {@link MotionEvent#ACTION_HOVER_MOVE}. + */ + public static final int ACTION_HOVER_MOVE = 7; + + /** + * Synonym for {@link MotionEvent#ACTION_SCROLL}. + */ + public static final int ACTION_SCROLL = 8; + + /** + * Synonym for {@link MotionEvent#ACTION_POINTER_INDEX_MASK}. + */ + public static final int ACTION_POINTER_INDEX_MASK = 0xff00; + + /** + * Synonym for {@link MotionEvent#ACTION_POINTER_INDEX_SHIFT}. + */ + public static final int ACTION_POINTER_INDEX_SHIFT = 8; + + /** + * Call {@link MotionEvent#getAction}, returning only the {@link #ACTION_MASK} + * portion. + */ + public static int getActionMasked(MotionEvent event) { + return event.getAction() & ACTION_MASK; + } + + /** + * Call {@link MotionEvent#getAction}, returning only the pointer index + * portion + */ + public static int getActionIndex(MotionEvent event) { + return (event.getAction() & ACTION_POINTER_INDEX_MASK) + >> ACTION_POINTER_INDEX_SHIFT; + } + + /** + * Call {@link MotionEvent#findPointerIndex(int)}. + * If running on a pre-{@android.os.Build.VERSION_CODES#HONEYCOMB} device, + * does nothing and returns -1. + */ + public static int findPointerIndex(MotionEvent event, int pointerId) { + return IMPL.findPointerIndex(event, pointerId); + } + + /** + * Call {@link MotionEvent#getPointerId(int)}. + * If running on a pre-{@android.os.Build.VERSION_CODES#HONEYCOMB} device, + * {@link IndexOutOfBoundsException} is thrown. + */ + public static int getPointerId(MotionEvent event, int pointerIndex) { + return IMPL.getPointerId(event, pointerIndex); + } + + /** + * Call {@link MotionEvent#getX(int)}. + * If running on a pre-{@android.os.Build.VERSION_CODES#HONEYCOMB} device, + * {@link IndexOutOfBoundsException} is thrown. + */ + public static float getX(MotionEvent event, int pointerIndex) { + return IMPL.getX(event, pointerIndex); + } + + /** + * Call {@link MotionEvent#getY(int)}. + * If running on a pre-{@android.os.Build.VERSION_CODES#HONEYCOMB} device, + * {@link IndexOutOfBoundsException} is thrown. + */ + public static float getY(MotionEvent event, int pointerIndex) { + return IMPL.getY(event, pointerIndex); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/MotionEventCompatEclair.java b/actionbarsherlock/library/src/android/support/v4/view/MotionEventCompatEclair.java new file mode 100644 index 000000000..dedb1d1d0 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/MotionEventCompatEclair.java @@ -0,0 +1,37 @@ +/* + * 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 android.support.v4.view; + +import android.view.MotionEvent; + +/** + * Implementation of motion event compatibility that can call Eclair APIs. + */ +class MotionEventCompatEclair { + public static int findPointerIndex(MotionEvent event, int pointerId) { + return event.findPointerIndex(pointerId); + } + public static int getPointerId(MotionEvent event, int pointerIndex) { + return event.getPointerId(pointerIndex); + } + public static float getX(MotionEvent event, int pointerIndex) { + return event.getX(pointerIndex); + } + public static float getY(MotionEvent event, int pointerIndex) { + return event.getY(pointerIndex); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/PagerAdapter.java b/actionbarsherlock/library/src/android/support/v4/view/PagerAdapter.java new file mode 100644 index 000000000..0be93a0d5 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/PagerAdapter.java @@ -0,0 +1,138 @@ +/* + * 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 android.support.v4.view; + +import android.os.Parcelable; +import android.view.View; + +/** + * Base class providing the adapter to populate pages inside of + * a {@link ViewPager}. You will most likely want to use a more + * specific implementation of this, such as + * {@link android.support.v4.app.FragmentPagerAdapter} or + * {@link android.support.v4.app.FragmentStatePagerAdapter}. + */ +public abstract class PagerAdapter { + private DataSetObserver mObserver; + + public static final int POSITION_UNCHANGED = -1; + public static final int POSITION_NONE = -2; + + /** + * Used to watch for changes within the adapter. + */ + interface DataSetObserver { + public void onDataSetChanged(); + } + + /** + * Return the number of views available. + */ + public abstract int getCount(); + + /** + * Called when a change in the shown pages is going to start being made. + * @param container The containing View which is displaying this adapter's + * page views. + */ + public abstract void startUpdate(View container); + + /** + * Create the page for the given position. The adapter is responsible + * for adding the view to the container given here, although it only + * must ensure this is done by the time it returns from + * {@link #finishUpdate()}. + * + * @param container The containing View in which the page will be shown. + * @param position The page position to be instantiated. + * @return Returns an Object representing the new page. This does not + * need to be a View, but can be some other container of the page. + */ + public abstract Object instantiateItem(View container, int position); + + /** + * Remove a page for the given position. The adapter is responsible + * for removing the view from its container, although it only must ensure + * this is done by the time it returns from {@link #finishUpdate()}. + * + * @param container The containing View from which the page will be removed. + * @param position The page position to be removed. + * @param object The same object that was returned by + * {@link #instantiateItem(View, int)}. + */ + public abstract void destroyItem(View container, int position, Object object); + + /** + * Called to inform the adapter of which item is currently considered to + * be the "primary", that is the one show to the user as the current page. + * + * @param container The containing View from which the page will be removed. + * @param position The page position that is now the primary. + * @param object The same object that was returned by + * {@link #instantiateItem(View, int)}. + */ + public void setPrimaryItem(View container, int position, Object object) { + } + + /** + * Called when the a change in the shown pages has been completed. At this + * point you must ensure that all of the pages have actually been added or + * removed from the container as appropriate. + * @param container The containing View which is displaying this adapter's + * page views. + */ + public abstract void finishUpdate(View container); + + public abstract boolean isViewFromObject(View view, Object object); + + public abstract Parcelable saveState(); + + public abstract void restoreState(Parcelable state, ClassLoader loader); + + /** + * Called when the host view is attempting to determine if an item's position + * has changed. Returns {@link #POSITION_UNCHANGED} if the position of the given + * item has not changed or {@link #POSITION_NONE} if the item is no longer present + * in the adapter. + * + *

The default implementation assumes that items will never + * change position and always returns {@link #POSITION_UNCHANGED}. + * + * @param object Object representing an item, previously returned by a call to + * {@link #instantiateItem(View, int)}. + * @return object's new position index from [0, {@link #getCount()}), + * {@link #POSITION_UNCHANGED} if the object's position has not changed, + * or {@link #POSITION_NONE} if the item is no longer present. + */ + public int getItemPosition(Object object) { + return POSITION_UNCHANGED; + } + + /** + * This method should be called by the application if the data backing this adapter has changed + * and associated views should update. + */ + public void notifyDataSetChanged() { + if (mObserver != null) { + mObserver.onDataSetChanged(); + } + } + + void setDataSetObserver(DataSetObserver observer) { + mObserver = observer; + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/SubMenu.java b/actionbarsherlock/library/src/android/support/v4/view/SubMenu.java new file mode 100644 index 000000000..953e193f7 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/SubMenu.java @@ -0,0 +1,30 @@ +package android.support.v4.view; + +import android.graphics.drawable.Drawable; +import android.view.View; + +public interface SubMenu extends android.view.SubMenu { + @Override + MenuItem getItem(); + + @Override + SubMenu setHeaderIcon(Drawable icon); + + @Override + SubMenu setHeaderIcon(int iconRes); + + @Override + SubMenu setHeaderTitle(CharSequence title); + + @Override + SubMenu setHeaderTitle(int titleRes); + + @Override + SubMenu setHeaderView(View view); + + @Override + SubMenu setIcon(Drawable icon); + + @Override + SubMenu setIcon(int iconRes); +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/VelocityTrackerCompat.java b/actionbarsherlock/library/src/android/support/v4/view/VelocityTrackerCompat.java new file mode 100644 index 000000000..8c8749061 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/VelocityTrackerCompat.java @@ -0,0 +1,92 @@ +/* + * 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 android.support.v4.view; + +import android.view.VelocityTracker; + +/** + * Helper for accessing newer features in VelocityTracker. + */ +public class VelocityTrackerCompat { + /** + * Interface for the full API. + */ + interface VelocityTrackerVersionImpl { + public float getXVelocity(VelocityTracker tracker, int pointerId); + public float getYVelocity(VelocityTracker tracker, int pointerId); + } + + /** + * Interface implementation that doesn't use anything about v4 APIs. + */ + static class BaseVelocityTrackerVersionImpl implements VelocityTrackerVersionImpl { + @Override + public float getXVelocity(VelocityTracker tracker, int pointerId) { + return tracker.getXVelocity(); + } + @Override + public float getYVelocity(VelocityTracker tracker, int pointerId) { + return tracker.getYVelocity(); + } + } + + /** + * Interface implementation for devices with at least v11 APIs. + */ + static class HoneycombVelocityTrackerVersionImpl implements VelocityTrackerVersionImpl { + @Override + public float getXVelocity(VelocityTracker tracker, int pointerId) { + return VelocityTrackerCompatHoneycomb.getXVelocity(tracker, pointerId); + } + @Override + public float getYVelocity(VelocityTracker tracker, int pointerId) { + return VelocityTrackerCompatHoneycomb.getYVelocity(tracker, pointerId); + } + } + + /** + * Select the correct implementation to use for the current platform. + */ + static final VelocityTrackerVersionImpl IMPL; + static { + if (android.os.Build.VERSION.SDK_INT >= 11) { + IMPL = new HoneycombVelocityTrackerVersionImpl(); + } else { + IMPL = new BaseVelocityTrackerVersionImpl(); + } + } + + // ------------------------------------------------------------------- + + /** + * Call {@link VelocityTracker#getXVelocity(int)}. + * If running on a pre-{@android.os.Build.VERSION_CODES#HONEYCOMB} device, + * returns {@link VelocityTracker#getXVelocity()}. + */ + public static float getXVelocity(VelocityTracker tracker, int pointerId) { + return IMPL.getXVelocity(tracker, pointerId); + } + + /** + * Call {@link VelocityTracker#getYVelocity(int)}. + * If running on a pre-{@android.os.Build.VERSION_CODES#HONEYCOMB} device, + * returns {@link VelocityTracker#getYVelocity()}. + */ + public static float getYVelocity(VelocityTracker tracker, int pointerId) { + return IMPL.getYVelocity(tracker, pointerId); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/VelocityTrackerCompatHoneycomb.java b/actionbarsherlock/library/src/android/support/v4/view/VelocityTrackerCompatHoneycomb.java new file mode 100644 index 000000000..4f9d326c9 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/VelocityTrackerCompatHoneycomb.java @@ -0,0 +1,31 @@ +/* + * 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 android.support.v4.view; + +import android.view.VelocityTracker; + +/** + * Implementation of velocity tracker compatibility that can call Honeycomb APIs. + */ +class VelocityTrackerCompatHoneycomb { + public static float getXVelocity(VelocityTracker tracker, int pointerId) { + return tracker.getXVelocity(pointerId); + } + public static float getYVelocity(VelocityTracker tracker, int pointerId) { + return tracker.getYVelocity(pointerId); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/ViewCompat.java b/actionbarsherlock/library/src/android/support/v4/view/ViewCompat.java new file mode 100644 index 000000000..33d0cd2b7 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/ViewCompat.java @@ -0,0 +1,163 @@ +/* + * 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 android.support.v4.view; + +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; + +/** + * Helper for accessing newer features in View. + */ +public class ViewCompat { + /** + * Always allow a user to over-scroll this view, provided it is a + * view that can scroll. + */ + public static final int OVER_SCROLL_ALWAYS = 0; + + /** + * Allow a user to over-scroll this view only if the content is large + * enough to meaningfully scroll, provided it is a view that can scroll. + */ + public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; + + /** + * Never allow a user to over-scroll this view. + */ + public static final int OVER_SCROLL_NEVER = 2; + + interface ViewCompatImpl { + public boolean canScrollHorizontally(View v, int direction); + public boolean canScrollVertically(View v, int direction); + public int getOverScrollMode(View v); + public void setOverScrollMode(View v, int mode); + public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event); + public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event); + public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info); + public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate); + } + + static class BaseViewCompatImpl implements ViewCompatImpl { + public boolean canScrollHorizontally(View v, int direction) { + return false; + } + public boolean canScrollVertically(View v, int direction) { + return false; + } + public int getOverScrollMode(View v) { + return OVER_SCROLL_NEVER; + } + public void setOverScrollMode(View v, int mode) { + // Do nothing; API doesn't exist + } + public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) { + // Do nothing; API doesn't exist + } + public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { + // Do nothing; API doesn't exist + } + public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { + // Do nothing; API doesn't exist + } + public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) { + // Do nothing; API doesn't exist + } + } + + static class GBViewCompatImpl extends BaseViewCompatImpl { + @Override + public int getOverScrollMode(View v) { + return ViewCompatGingerbread.getOverScrollMode(v); + } + @Override + public void setOverScrollMode(View v, int mode) { + ViewCompatGingerbread.setOverScrollMode(v, mode); + } + } +/* + static class ICSViewCompatImpl extends GBViewCompatImpl { + @Override + public boolean canScrollHorizontally(View v, int direction) { + return ViewCompatICS.canScrollHorizontally(v, direction); + } + @Override + public boolean canScrollVertically(View v, int direction) { + return ViewCompatICS.canScrollVertically(v, direction); + } + @Override + public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { + ViewCompatICS.onPopulateAccessibilityEvent(v, event); + } + @Override + public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { + ViewCompatICS.onInitializeAccessibilityEvent(v, event); + } + @Override + public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) { + ViewCompatICS.onInitializeAccessibilityNodeInfo(v, info.getImpl()); + } + @Override + public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) { + ViewCompatICS.setAccessibilityDelegate(v, delegate.getBridge()); + } + } +*/ + static final ViewCompatImpl IMPL; + static { + final int version = android.os.Build.VERSION.SDK_INT; + /*if (version >= 14) { + IMPL = new ICSViewCompatImpl(); + } else*/ if (version >= 9) { + IMPL = new GBViewCompatImpl(); + } else { + IMPL = new BaseViewCompatImpl(); + } + } + + public static boolean canScrollHorizontally(View v, int direction) { + return IMPL.canScrollHorizontally(v, direction); + } + + public static boolean canScrollVertically(View v, int direction) { + return IMPL.canScrollVertically(v, direction); + } + + public static int getOverScrollMode(View v) { + return IMPL.getOverScrollMode(v); + } + + public static void setOverScrollMode(View v, int mode) { + IMPL.setOverScrollMode(v, mode); + } + + public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { + IMPL.onPopulateAccessibilityEvent(v, event); + } + + public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { + IMPL.onInitializeAccessibilityEvent(v, event); + } + + public static void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) { + IMPL.onInitializeAccessibilityNodeInfo(v, info); + } + + public static void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) { + IMPL.setAccessibilityDelegate(v, delegate); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/ViewCompatGingerbread.java b/actionbarsherlock/library/src/android/support/v4/view/ViewCompatGingerbread.java new file mode 100644 index 000000000..e063778d3 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/ViewCompatGingerbread.java @@ -0,0 +1,29 @@ +/* + * 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 android.support.v4.view; + +import android.view.View; + +public class ViewCompatGingerbread { + public static int getOverScrollMode(View v) { + return v.getOverScrollMode(); + } + + public static void setOverScrollMode(View v, int mode) { + v.setOverScrollMode(mode); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/ViewCompatICS.java b/actionbarsherlock/library/src/android/support/v4/view/ViewCompatICS.java new file mode 100644 index 000000000..da80d785b --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/ViewCompatICS.java @@ -0,0 +1,53 @@ +/* + * 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 android.support.v4.view; + +//import android.view.View; +//import android.view.View.AccessibilityDelegate; +//import android.view.accessibility.AccessibilityEvent; +//import android.view.accessibility.AccessibilityNodeInfo; + +/** + * Helper for accessing newer features in View introduced in ICS. + */ +class ViewCompatICS { +/* + public static boolean canScrollHorizontally(View v, int direction) { + return v.canScrollHorizontally(direction); + } + + public static boolean canScrollVertically(View v, int direction) { + return v.canScrollVertically(direction); + } + + public static void setAccessibilityDelegate(View v, Object delegate) { + v.setAccessibilityDelegate((AccessibilityDelegate) delegate); + } + + public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { + v.onPopulateAccessibilityEvent(event); + } + + public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { + v.onInitializeAccessibilityEvent(event); + } + + public static void onInitializeAccessibilityNodeInfo(View v, Object info) { + v.onInitializeAccessibilityNodeInfo((AccessibilityNodeInfo) info); + } +*/ +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/ViewConfigurationCompat.java b/actionbarsherlock/library/src/android/support/v4/view/ViewConfigurationCompat.java new file mode 100644 index 000000000..144466212 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/ViewConfigurationCompat.java @@ -0,0 +1,74 @@ +/* + * 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 android.support.v4.view; + +import android.view.ViewConfiguration; + +/** + * Helper for accessing newer features in ViewConfiguration. + */ +public class ViewConfigurationCompat { + /** + * Interface for the full API. + */ + interface ViewConfigurationVersionImpl { + public int getScaledPagingTouchSlop(ViewConfiguration config); + } + + /** + * Interface implementation that doesn't use anything about v4 APIs. + */ + static class BaseViewConfigurationVersionImpl implements ViewConfigurationVersionImpl { + @Override + public int getScaledPagingTouchSlop(ViewConfiguration config) { + return config.getScaledTouchSlop(); + } + } + + /** + * Interface implementation for devices with at least v11 APIs. + */ + static class FroyoViewConfigurationVersionImpl implements ViewConfigurationVersionImpl { + @Override + public int getScaledPagingTouchSlop(ViewConfiguration config) { + return ViewConfigurationCompatFroyo.getScaledPagingTouchSlop(config); + } + } + + /** + * Select the correct implementation to use for the current platform. + */ + static final ViewConfigurationVersionImpl IMPL; + static { + if (android.os.Build.VERSION.SDK_INT >= 11) { + IMPL = new FroyoViewConfigurationVersionImpl(); + } else { + IMPL = new BaseViewConfigurationVersionImpl(); + } + } + + // ------------------------------------------------------------------- + + /** + * Call {@link ViewConfiguration#getScaledPagingTouchSlop()}. + * If running on a pre-{@android.os.Build.VERSION_CODES#FROYO} device, + * returns {@link ViewConfiguration#getScaledTouchSlop()}. + */ + public static int getScaledPagingTouchSlop(ViewConfiguration config) { + return IMPL.getScaledPagingTouchSlop(config); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/ViewConfigurationCompatFroyo.java b/actionbarsherlock/library/src/android/support/v4/view/ViewConfigurationCompatFroyo.java new file mode 100644 index 000000000..e97a2a2ee --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/ViewConfigurationCompatFroyo.java @@ -0,0 +1,28 @@ +/* + * 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 android.support.v4.view; + +import android.view.ViewConfiguration; + +/** + * Implementation of menu compatibility that can call Honeycomb APIs. + */ +class ViewConfigurationCompatFroyo { + public static int getScaledPagingTouchSlop(ViewConfiguration config) { + return config.getScaledPagingTouchSlop(); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/ViewGroupCompat.java b/actionbarsherlock/library/src/android/support/v4/view/ViewGroupCompat.java new file mode 100644 index 000000000..162554f32 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/ViewGroupCompat.java @@ -0,0 +1,84 @@ +/* + * 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 android.support.v4.view; + +//import android.os.Build; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; + +/** + * Helper for accessing newer features in ViewGroup. + */ +public class ViewGroupCompat { + + interface ViewGroupCompatImpl { + public boolean onRequestSendAccessibilityEvent(ViewGroup group, View child, + AccessibilityEvent event); + } + + static class ViewGroupCompatStubImpl implements ViewGroupCompatImpl { + public boolean onRequestSendAccessibilityEvent( + ViewGroup group, View child, AccessibilityEvent event) { + return true; + } + } +/* + static class ViewGroupCompatIcsImpl extends ViewGroupCompatStubImpl { + @Override + public boolean onRequestSendAccessibilityEvent( + ViewGroup group, View child, AccessibilityEvent event) { + return ViewGroupCompatIcs.onRequestSendAccessibilityEvent(group, child, event); + } + } +*/ + static final ViewGroupCompatImpl IMPL; + static { + //if (Build.VERSION.SDK_INT >= 14) { + // IMPL = new ViewGroupCompatIcsImpl(); + //} else { + IMPL = new ViewGroupCompatStubImpl(); + //} + } + + /* + * Hide the constructor. + */ + private ViewGroupCompat() { + + } + + /** + * Called when a child has requested sending an {@link AccessibilityEvent} and + * gives an opportunity to its parent to augment the event. + *

+ * If an {@link AccessibilityDelegateCompat} has been specified via calling + * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its + * {@link AccessibilityDelegateCompat#onRequestSendAccessibilityEvent(ViewGroup, View, + * AccessibilityEvent)} is responsible for handling this call. + *

+ * + * @param group The group whose method to invoke. + * @param child The child which requests sending the event. + * @param event The event to be sent. + * @return True if the event should be sent. + */ + public static boolean onRequestSendAccessibilityEvent(ViewGroup group, View child, + AccessibilityEvent event) { + return IMPL.onRequestSendAccessibilityEvent(group, child, event); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/ViewGroupCompatIcs.java b/actionbarsherlock/library/src/android/support/v4/view/ViewGroupCompatIcs.java new file mode 100644 index 000000000..a5d5ff452 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/ViewGroupCompatIcs.java @@ -0,0 +1,33 @@ +/* + * 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 android.support.v4.view; + +//import android.view.View; +//import android.view.ViewGroup; +//import android.view.accessibility.AccessibilityEvent; + +/** + * ICS specific ViewGroup API implementation. + */ +class ViewGroupCompatIcs { +/* + public static boolean onRequestSendAccessibilityEvent(ViewGroup group, View child, + AccessibilityEvent event) { + return group.onRequestSendAccessibilityEvent(child, event); + } +*/ +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/ViewPager.java b/actionbarsherlock/library/src/android/support/v4/view/ViewPager.java new file mode 100644 index 000000000..9aa7b0eb1 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/ViewPager.java @@ -0,0 +1,1730 @@ +/* + * 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 android.support.v4.view; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.support.v4.os.ParcelableCompat; +import android.support.v4.os.ParcelableCompatCreatorCallbacks; +import android.support.v4.widget.EdgeEffectCompat; +import android.util.AttributeSet; +import android.util.Log; +import android.view.FocusFinder; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SoundEffectConstants; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.accessibility.AccessibilityEvent; +import android.view.animation.Interpolator; +import android.widget.Scroller; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +/** + * Layout manager that allows the user to flip left and right + * through pages of data. You supply an implementation of a + * {@link PagerAdapter} to generate the pages that the view shows. + * + *

Note this class is currently under early design and + * development. The API will likely change in later updates of + * the compatibility library, requiring changes to the source code + * of apps when they are compiled against the newer version.

+ */ +public class ViewPager extends ViewGroup { + private static final String TAG = "ViewPager"; + private static final boolean DEBUG = false; + + private static final boolean USE_CACHE = false; + + private static final int DEFAULT_OFFSCREEN_PAGES = 1; + private static final int MAX_SETTLE_DURATION = 600; // ms + + static class ItemInfo { + Object object; + int position; + boolean scrolling; + } + + private static final Comparator COMPARATOR = new Comparator(){ + @Override + public int compare(ItemInfo lhs, ItemInfo rhs) { + return lhs.position - rhs.position; + }}; + + private static final Interpolator sInterpolator = new Interpolator() { + public float getInterpolation(float t) { + // _o(t) = t * t * ((tension + 1) * t + tension) + // o(t) = _o(t - 1) + 1 + t -= 1.0f; + return t * t * t + 1.0f; + } + }; + + private final ArrayList mItems = new ArrayList(); + + private PagerAdapter mAdapter; + private int mCurItem; // Index of currently displayed page. + private int mRestoredCurItem = -1; + private Parcelable mRestoredAdapterState = null; + private ClassLoader mRestoredClassLoader = null; + private Scroller mScroller; + private PagerAdapter.DataSetObserver mObserver; + + private int mPageMargin; + private Drawable mMarginDrawable; + + private int mChildWidthMeasureSpec; + private int mChildHeightMeasureSpec; + private boolean mInLayout; + + private boolean mScrollingCacheEnabled; + + private boolean mPopulatePending; + private boolean mScrolling; + private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES; + + private boolean mIsBeingDragged; + private boolean mIsUnableToDrag; + private int mTouchSlop; + private float mInitialMotionX; + /** + * Position of the last motion event. + */ + private float mLastMotionX; + private float mLastMotionY; + /** + * ID of the active pointer. This is used to retain consistency during + * drags/flings if multiple pointers are used. + */ + private int mActivePointerId = INVALID_POINTER; + /** + * Sentinel value for no current active pointer. + * Used by {@link #mActivePointerId}. + */ + private static final int INVALID_POINTER = -1; + + /** + * Determines speed during touch scrolling + */ + private VelocityTracker mVelocityTracker; + private int mMinimumVelocity; + private int mMaximumVelocity; + private float mBaseLineFlingVelocity; + private float mFlingVelocityInfluence; + + private boolean mFakeDragging; + private long mFakeDragBeginTime; + + private EdgeEffectCompat mLeftEdge; + private EdgeEffectCompat mRightEdge; + + private boolean mFirstLayout = true; + + private OnPageChangeListener mOnPageChangeListener; + + /** + * Indicates that the pager is in an idle, settled state. The current page + * is fully in view and no animation is in progress. + */ + public static final int SCROLL_STATE_IDLE = 0; + + /** + * Indicates that the pager is currently being dragged by the user. + */ + public static final int SCROLL_STATE_DRAGGING = 1; + + /** + * Indicates that the pager is in the process of settling to a final position. + */ + public static final int SCROLL_STATE_SETTLING = 2; + + private int mScrollState = SCROLL_STATE_IDLE; + + /** + * Callback interface for responding to changing state of the selected page. + */ + public interface OnPageChangeListener { + + /** + * This method will be invoked when the current page is scrolled, either as part + * of a programmatically initiated smooth scroll or a user initiated touch scroll. + * + * @param position Position index of the first page currently being displayed. + * Page position+1 will be visible if positionOffset is nonzero. + * @param positionOffset Value from [0, 1) indicating the offset from the page at position. + * @param positionOffsetPixels Value in pixels indicating the offset from position. + */ + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels); + + /** + * This method will be invoked when a new page becomes selected. Animation is not + * necessarily complete. + * + * @param position Position index of the new selected page. + */ + public void onPageSelected(int position); + + /** + * Called when the scroll state changes. Useful for discovering when the user + * begins dragging, when the pager is automatically settling to the current page, + * or when it is fully stopped/idle. + * + * @param state The new scroll state. + * @see ViewPager#SCROLL_STATE_IDLE + * @see ViewPager#SCROLL_STATE_DRAGGING + * @see ViewPager#SCROLL_STATE_SETTLING + */ + public void onPageScrollStateChanged(int state); + } + + /** + * Simple implementation of the {@link OnPageChangeListener} interface with stub + * implementations of each method. Extend this if you do not intend to override + * every method of {@link OnPageChangeListener}. + */ + public static class SimpleOnPageChangeListener implements OnPageChangeListener { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + // This space for rent + } + + @Override + public void onPageSelected(int position) { + // This space for rent + } + + @Override + public void onPageScrollStateChanged(int state) { + // This space for rent + } + } + + public ViewPager(Context context) { + super(context); + initViewPager(); + } + + public ViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + initViewPager(); + } + + void initViewPager() { + setWillNotDraw(false); + setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); + setFocusable(true); + final Context context = getContext(); + mScroller = new Scroller(context, sInterpolator); + final ViewConfiguration configuration = ViewConfiguration.get(context); + mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); + mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); + mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); + mLeftEdge = new EdgeEffectCompat(context); + mRightEdge = new EdgeEffectCompat(context); + + float density = context.getResources().getDisplayMetrics().density; + mBaseLineFlingVelocity = 2500.0f * density; + mFlingVelocityInfluence = 0.4f; + } + + private void setScrollState(int newState) { + if (mScrollState == newState) { + return; + } + + mScrollState = newState; + if (mOnPageChangeListener != null) { + mOnPageChangeListener.onPageScrollStateChanged(newState); + } + } + + public void setAdapter(PagerAdapter adapter) { + if (mAdapter != null) { + mAdapter.setDataSetObserver(null); + mAdapter.startUpdate(this); + for (int i = 0; i < mItems.size(); i++) { + final ItemInfo ii = mItems.get(i); + mAdapter.destroyItem(this, ii.position, ii.object); + } + mAdapter.finishUpdate(this); + mItems.clear(); + removeAllViews(); + mCurItem = 0; + scrollTo(0, 0); + } + + mAdapter = adapter; + + if (mAdapter != null) { + if (mObserver == null) { + mObserver = new DataSetObserver(); + } + mAdapter.setDataSetObserver(mObserver); + mPopulatePending = false; + if (mRestoredCurItem >= 0) { + mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader); + setCurrentItemInternal(mRestoredCurItem, false, true); + mRestoredCurItem = -1; + mRestoredAdapterState = null; + mRestoredClassLoader = null; + } else { + populate(); + } + } + } + + public PagerAdapter getAdapter() { + return mAdapter; + } + + /** + * Set the currently selected page. If the ViewPager has already been through its first + * layout there will be a smooth animated transition between the current item and the + * specified item. + * + * @param item Item index to select + */ + public void setCurrentItem(int item) { + mPopulatePending = false; + setCurrentItemInternal(item, !mFirstLayout, false); + } + + /** + * Set the currently selected page. + * + * @param item Item index to select + * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately + */ + public void setCurrentItem(int item, boolean smoothScroll) { + mPopulatePending = false; + setCurrentItemInternal(item, smoothScroll, false); + } + + public int getCurrentItem() { + return mCurItem; + } + + void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) { + setCurrentItemInternal(item, smoothScroll, always, 0); + } + + void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) { + if (mAdapter == null || mAdapter.getCount() <= 0) { + setScrollingCacheEnabled(false); + return; + } + if (!always && mCurItem == item && mItems.size() != 0) { + setScrollingCacheEnabled(false); + return; + } + if (item < 0) { + item = 0; + } else if (item >= mAdapter.getCount()) { + item = mAdapter.getCount() - 1; + } + final int pageLimit = mOffscreenPageLimit; + if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) { + // We are doing a jump by more than one page. To avoid + // glitches, we want to keep all current pages in the view + // until the scroll ends. + for (int i=0; iThis is offered as an optimization. If you know in advance the number + * of pages you will need to support or have lazy-loading mechanisms in place + * on your pages, tweaking this setting can have benefits in perceived smoothness + * of paging animations and interaction. If you have a small number of pages (3-4) + * that you can keep active all at once, less time will be spent in layout for + * newly created view subtrees as the user pages back and forth.

+ * + *

You should keep this limit low, especially if your pages have complex layouts. + * This setting defaults to 1.

+ * + * @param limit How many pages will be kept offscreen in an idle state. + */ + public void setOffscreenPageLimit(int limit) { + if (limit < DEFAULT_OFFSCREEN_PAGES) { + Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + + DEFAULT_OFFSCREEN_PAGES); + limit = DEFAULT_OFFSCREEN_PAGES; + } + if (limit != mOffscreenPageLimit) { + mOffscreenPageLimit = limit; + populate(); + } + } + + /** + * Set the margin between pages. + * + * @param marginPixels Distance between adjacent pages in pixels + * @see #getPageMargin() + * @see #setPageMarginDrawable(Drawable) + * @see #setPageMarginDrawable(int) + */ + public void setPageMargin(int marginPixels) { + final int oldMargin = mPageMargin; + mPageMargin = marginPixels; + + final int width = getWidth(); + recomputeScrollPosition(width, width, marginPixels, oldMargin); + + requestLayout(); + } + + /** + * Return the margin between pages. + * + * @return The size of the margin in pixels + */ + public int getPageMargin() { + return mPageMargin; + } + + /** + * Set a drawable that will be used to fill the margin between pages. + * + * @param d Drawable to display between pages + */ + public void setPageMarginDrawable(Drawable d) { + mMarginDrawable = d; + if (d != null) refreshDrawableState(); + setWillNotDraw(d == null); + invalidate(); + } + + /** + * Set a drawable that will be used to fill the margin between pages. + * + * @param resId Resource ID of a drawable to display between pages + */ + public void setPageMarginDrawable(int resId) { + setPageMarginDrawable(getContext().getResources().getDrawable(resId)); + } + + @Override + protected boolean verifyDrawable(Drawable who) { + return super.verifyDrawable(who) || who == mMarginDrawable; + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + final Drawable d = mMarginDrawable; + if (d != null && d.isStateful()) { + d.setState(getDrawableState()); + } + } + + // We want the duration of the page snap animation to be influenced by the distance that + // the screen has to travel, however, we don't want this duration to be effected in a + // purely linear fashion. Instead, we use this method to moderate the effect that the distance + // of travel has on the overall snap duration. + float distanceInfluenceForSnapDuration(float f) { + f -= 0.5f; // center the values about 0. + f *= 0.3f * Math.PI / 2.0f; + return (float) Math.sin(f); + } + + /** + * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. + * + * @param x the number of pixels to scroll by on the X axis + * @param y the number of pixels to scroll by on the Y axis + */ + void smoothScrollTo(int x, int y) { + smoothScrollTo(x, y, 0); + } + + /** + * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. + * + * @param x the number of pixels to scroll by on the X axis + * @param y the number of pixels to scroll by on the Y axis + * @param velocity the velocity associated with a fling, if applicable. (0 otherwise) + */ + void smoothScrollTo(int x, int y, int velocity) { + if (getChildCount() == 0) { + // Nothing to do. + setScrollingCacheEnabled(false); + return; + } + int sx = getScrollX(); + int sy = getScrollY(); + int dx = x - sx; + int dy = y - sy; + if (dx == 0 && dy == 0) { + completeScroll(); + setScrollState(SCROLL_STATE_IDLE); + return; + } + + setScrollingCacheEnabled(true); + mScrolling = true; + setScrollState(SCROLL_STATE_SETTLING); + + final float pageDelta = (float) Math.abs(dx) / (getWidth() + mPageMargin); + int duration = (int) (pageDelta * 100); + + velocity = Math.abs(velocity); + if (velocity > 0) { + duration += (duration / (velocity / mBaseLineFlingVelocity)) * mFlingVelocityInfluence; + } else { + duration += 100; + } + duration = Math.min(duration, MAX_SETTLE_DURATION); + + mScroller.startScroll(sx, sy, dx, dy, duration); + invalidate(); + } + + void addNewItem(int position, int index) { + ItemInfo ii = new ItemInfo(); + ii.position = position; + ii.object = mAdapter.instantiateItem(this, position); + if (index < 0) { + mItems.add(ii); + } else { + mItems.add(index, ii); + } + } + + void dataSetChanged() { + // This method only gets called if our observer is attached, so mAdapter is non-null. + + boolean needPopulate = mItems.size() < 3 && mItems.size() < mAdapter.getCount(); + int newCurrItem = -1; + + for (int i = 0; i < mItems.size(); i++) { + final ItemInfo ii = mItems.get(i); + final int newPos = mAdapter.getItemPosition(ii.object); + + if (newPos == PagerAdapter.POSITION_UNCHANGED) { + continue; + } + + if (newPos == PagerAdapter.POSITION_NONE) { + mItems.remove(i); + i--; + mAdapter.destroyItem(this, ii.position, ii.object); + needPopulate = true; + + if (mCurItem == ii.position) { + // Keep the current item in the valid range + newCurrItem = Math.max(0, Math.min(mCurItem, mAdapter.getCount() - 1)); + } + continue; + } + + if (ii.position != newPos) { + if (ii.position == mCurItem) { + // Our current item changed position. Follow it. + newCurrItem = newPos; + } + + ii.position = newPos; + needPopulate = true; + } + } + + Collections.sort(mItems, COMPARATOR); + + if (newCurrItem >= 0) { + // TODO This currently causes a jump. + setCurrentItemInternal(newCurrItem, false, true); + needPopulate = true; + } + if (needPopulate) { + populate(); + requestLayout(); + } + } + + void populate() { + if (mAdapter == null) { + return; + } + + // Bail now if we are waiting to populate. This is to hold off + // on creating views from the time the user releases their finger to + // fling to a new position until we have finished the scroll to + // that position, avoiding glitches from happening at that point. + if (mPopulatePending) { + if (DEBUG) Log.i(TAG, "populate is pending, skipping for now..."); + return; + } + + // Also, don't populate until we are attached to a window. This is to + // avoid trying to populate before we have restored our view hierarchy + // state and conflicting with what is restored. + if (getWindowToken() == null) { + return; + } + + mAdapter.startUpdate(this); + + final int pageLimit = mOffscreenPageLimit; + final int startPos = Math.max(0, mCurItem - pageLimit); + final int N = mAdapter.getCount(); + final int endPos = Math.min(N-1, mCurItem + pageLimit); + + if (DEBUG) Log.v(TAG, "populating: startPos=" + startPos + " endPos=" + endPos); + + // Add and remove pages in the existing list. + int lastPos = -1; + for (int i=0; i endPos) && !ii.scrolling) { + if (DEBUG) Log.i(TAG, "removing: " + ii.position + " @ " + i); + mItems.remove(i); + i--; + mAdapter.destroyItem(this, ii.position, ii.object); + } else if (lastPos < endPos && ii.position > startPos) { + // The next item is outside of our range, but we have a gap + // between it and the last item where we want to have a page + // shown. Fill in the gap. + lastPos++; + if (lastPos < startPos) { + lastPos = startPos; + } + while (lastPos <= endPos && lastPos < ii.position) { + if (DEBUG) Log.i(TAG, "inserting: " + lastPos + " @ " + i); + addNewItem(lastPos, i); + lastPos++; + i++; + } + } + lastPos = ii.position; + } + + // Add any new pages we need at the end. + lastPos = mItems.size() > 0 ? mItems.get(mItems.size()-1).position : -1; + if (lastPos < endPos) { + lastPos++; + lastPos = lastPos > startPos ? lastPos : startPos; + while (lastPos <= endPos) { + if (DEBUG) Log.i(TAG, "appending: " + lastPos); + addNewItem(lastPos, -1); + lastPos++; + } + } + + if (DEBUG) { + Log.i(TAG, "Current page list:"); + for (int i=0; i CREATOR + = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks() { + @Override + public SavedState createFromParcel(Parcel in, ClassLoader loader) { + return new SavedState(in, loader); + } + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }); + + SavedState(Parcel in, ClassLoader loader) { + super(in); + if (loader == null) { + loader = getClass().getClassLoader(); + } + position = in.readInt(); + adapterState = in.readParcelable(loader); + this.loader = loader; + } + } + + @Override + public Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + SavedState ss = new SavedState(superState); + ss.position = mCurItem; + if (mAdapter != null) { + ss.adapterState = mAdapter.saveState(); + } + return ss; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + if (!(state instanceof SavedState)) { + super.onRestoreInstanceState(state); + return; + } + + SavedState ss = (SavedState)state; + super.onRestoreInstanceState(ss.getSuperState()); + + if (mAdapter != null) { + mAdapter.restoreState(ss.adapterState, ss.loader); + setCurrentItemInternal(ss.position, false, true); + } else { + mRestoredCurItem = ss.position; + mRestoredAdapterState = ss.adapterState; + mRestoredClassLoader = ss.loader; + } + } + + @Override + public void addView(View child, int index, LayoutParams params) { + if (mInLayout) { + addViewInLayout(child, index, params); + child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec); + } else { + super.addView(child, index, params); + } + + if (USE_CACHE) { + if (child.getVisibility() != GONE) { + child.setDrawingCacheEnabled(mScrollingCacheEnabled); + } else { + child.setDrawingCacheEnabled(false); + } + } + } + + ItemInfo infoForChild(View child) { + for (int i=0; i 0) { + final int oldScrollPos = getScrollX(); + final int oldwwm = oldWidth + oldMargin; + final int oldScrollItem = oldScrollPos / oldwwm; + final float scrollOffset = (float) (oldScrollPos % oldwwm) / oldwwm; + final int scrollPos = (int) ((oldScrollItem + scrollOffset) * widthWithMargin); + scrollTo(scrollPos, getScrollY()); + if (!mScroller.isFinished()) { + // We now return to your regularly scheduled scroll, already in progress. + final int newDuration = mScroller.getDuration() - mScroller.timePassed(); + mScroller.startScroll(scrollPos, 0, mCurItem * widthWithMargin, 0, newDuration); + } + } else { + int scrollPos = mCurItem * widthWithMargin; + if (scrollPos != getScrollX()) { + completeScroll(); + scrollTo(scrollPos, getScrollY()); + } + } + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + mInLayout = true; + populate(); + mInLayout = false; + + final int count = getChildCount(); + final int width = r-l; + + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + ItemInfo ii; + if (child.getVisibility() != GONE && (ii=infoForChild(child)) != null) { + int loff = (width + mPageMargin) * ii.position; + int childLeft = getPaddingLeft() + loff; + int childTop = getPaddingTop(); + if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object + + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth() + + "x" + child.getMeasuredHeight()); + child.layout(childLeft, childTop, + childLeft + child.getMeasuredWidth(), + childTop + child.getMeasuredHeight()); + } + } + mFirstLayout = false; + } + + @Override + public void computeScroll() { + if (DEBUG) Log.i(TAG, "computeScroll: finished=" + mScroller.isFinished()); + if (!mScroller.isFinished()) { + if (mScroller.computeScrollOffset()) { + if (DEBUG) Log.i(TAG, "computeScroll: still scrolling"); + int oldX = getScrollX(); + int oldY = getScrollY(); + int x = mScroller.getCurrX(); + int y = mScroller.getCurrY(); + + if (oldX != x || oldY != y) { + scrollTo(x, y); + } + + if (mOnPageChangeListener != null) { + final int widthWithMargin = getWidth() + mPageMargin; + final int position = x / widthWithMargin; + final int offsetPixels = x % widthWithMargin; + final float offset = (float) offsetPixels / widthWithMargin; + mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels); + } + + // Keep on drawing until the animation has finished. + invalidate(); + return; + } + } + + // Done with scroll, clean up state. + completeScroll(); + } + + private void completeScroll() { + boolean needPopulate = mScrolling; + if (needPopulate) { + // Done with scroll, no longer want to cache view drawing. + setScrollingCacheEnabled(false); + mScroller.abortAnimation(); + int oldX = getScrollX(); + int oldY = getScrollY(); + int x = mScroller.getCurrX(); + int y = mScroller.getCurrY(); + if (oldX != x || oldY != y) { + scrollTo(x, y); + } + setScrollState(SCROLL_STATE_IDLE); + } + mPopulatePending = false; + mScrolling = false; + for (int i=0; i 0 && scrollX == 0) || (dx < 0 && mAdapter != null && + // scrollX >= (mAdapter.getCount() - 1) * getWidth() - 1); + if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff); + + if (canScroll(this, false, (int) dx, (int) x, (int) y)) { + // Nested view has scrollable area under this point. Let it be handled there. + mInitialMotionX = mLastMotionX = x; + mLastMotionY = y; + return false; + } + if (xDiff > mTouchSlop && xDiff > yDiff) { + if (DEBUG) Log.v(TAG, "Starting drag!"); + mIsBeingDragged = true; + setScrollState(SCROLL_STATE_DRAGGING); + mLastMotionX = x; + setScrollingCacheEnabled(true); + } else { + if (yDiff > mTouchSlop) { + // The finger has moved enough in the vertical + // direction to be counted as a drag... abort + // any attempt to drag horizontally, to work correctly + // with children that have scrolling containers. + if (DEBUG) Log.v(TAG, "Starting unable to drag!"); + mIsUnableToDrag = true; + } + } + break; + } + + case MotionEvent.ACTION_DOWN: { + /* + * Remember location of down touch. + * ACTION_DOWN always refers to pointer index 0. + */ + mLastMotionX = mInitialMotionX = ev.getX(); + mLastMotionY = ev.getY(); + mActivePointerId = MotionEventCompat.getPointerId(ev, 0); + + if (mScrollState == SCROLL_STATE_SETTLING) { + // Let the user 'catch' the pager as it animates. + mIsBeingDragged = true; + mIsUnableToDrag = false; + setScrollState(SCROLL_STATE_DRAGGING); + } else { + completeScroll(); + mIsBeingDragged = false; + mIsUnableToDrag = false; + } + + if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY + + " mIsBeingDragged=" + mIsBeingDragged + + "mIsUnableToDrag=" + mIsUnableToDrag); + break; + } + + case MotionEventCompat.ACTION_POINTER_UP: + onSecondaryPointerUp(ev); + break; + } + + /* + * The only time we want to intercept motion events is if we are in the + * drag mode. + */ + return mIsBeingDragged; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (mFakeDragging) { + // A fake drag is in progress already, ignore this real one + // but still eat the touch events. + // (It is likely that the user is multi-touching the screen.) + return true; + } + + if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { + // Don't handle edge touches immediately -- they may actually belong to one of our + // descendants. + return false; + } + + if (mAdapter == null || mAdapter.getCount() == 0) { + // Nothing to present or scroll; nothing to touch. + return false; + } + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(ev); + + final int action = ev.getAction(); + boolean needsInvalidate = false; + + switch (action & MotionEventCompat.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: { + /* + * If being flinged and user touches, stop the fling. isFinished + * will be false if being flinged. + */ + completeScroll(); + + // Remember where the motion event started + mLastMotionX = mInitialMotionX = ev.getX(); + mActivePointerId = MotionEventCompat.getPointerId(ev, 0); + break; + } + case MotionEvent.ACTION_MOVE: + if (!mIsBeingDragged) { + final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); + final float x = MotionEventCompat.getX(ev, pointerIndex); + final float xDiff = Math.abs(x - mLastMotionX); + final float y = MotionEventCompat.getY(ev, pointerIndex); + final float yDiff = Math.abs(y - mLastMotionY); + if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff); + if (xDiff > mTouchSlop && xDiff > yDiff) { + if (DEBUG) Log.v(TAG, "Starting drag!"); + mIsBeingDragged = true; + mLastMotionX = x; + setScrollState(SCROLL_STATE_DRAGGING); + setScrollingCacheEnabled(true); + } + } + if (mIsBeingDragged) { + // Scroll to follow the motion event + final int activePointerIndex = MotionEventCompat.findPointerIndex( + ev, mActivePointerId); + final float x = MotionEventCompat.getX(ev, activePointerIndex); + final float deltaX = mLastMotionX - x; + mLastMotionX = x; + float oldScrollX = getScrollX(); + float scrollX = oldScrollX + deltaX; + final int width = getWidth(); + final int widthWithMargin = width + mPageMargin; + + final int lastItemIndex = mAdapter.getCount() - 1; + final float leftBound = Math.max(0, (mCurItem - 1) * widthWithMargin); + final float rightBound = + Math.min(mCurItem + 1, lastItemIndex) * widthWithMargin; + if (scrollX < leftBound) { + if (leftBound == 0) { + float over = -scrollX; + needsInvalidate = mLeftEdge.onPull(over / width); + } + scrollX = leftBound; + } else if (scrollX > rightBound) { + if (rightBound == lastItemIndex * widthWithMargin) { + float over = scrollX - rightBound; + needsInvalidate = mRightEdge.onPull(over / width); + } + scrollX = rightBound; + } + // Don't lose the rounded component + mLastMotionX += scrollX - (int) scrollX; + scrollTo((int) scrollX, getScrollY()); + if (mOnPageChangeListener != null) { + final int position = (int) scrollX / widthWithMargin; + final int positionOffsetPixels = (int) scrollX % widthWithMargin; + final float positionOffset = (float) positionOffsetPixels / widthWithMargin; + mOnPageChangeListener.onPageScrolled(position, positionOffset, + positionOffsetPixels); + } + } + break; + case MotionEvent.ACTION_UP: + if (mIsBeingDragged) { + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + int initialVelocity = (int) VelocityTrackerCompat.getXVelocity( + velocityTracker, mActivePointerId); + mPopulatePending = true; + final int widthWithMargin = getWidth() + mPageMargin; + final int scrollX = getScrollX(); + final int currentPage = scrollX / widthWithMargin; + int nextPage = initialVelocity > 0 ? currentPage : currentPage + 1; + setCurrentItemInternal(nextPage, true, true, initialVelocity); + + mActivePointerId = INVALID_POINTER; + endDrag(); + needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease(); + } + break; + case MotionEvent.ACTION_CANCEL: + if (mIsBeingDragged) { + setCurrentItemInternal(mCurItem, true, true); + mActivePointerId = INVALID_POINTER; + endDrag(); + needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease(); + } + break; + case MotionEventCompat.ACTION_POINTER_DOWN: { + final int index = MotionEventCompat.getActionIndex(ev); + final float x = MotionEventCompat.getX(ev, index); + mLastMotionX = x; + mActivePointerId = MotionEventCompat.getPointerId(ev, index); + break; + } + case MotionEventCompat.ACTION_POINTER_UP: + onSecondaryPointerUp(ev); + mLastMotionX = MotionEventCompat.getX(ev, + MotionEventCompat.findPointerIndex(ev, mActivePointerId)); + break; + } + if (needsInvalidate) { + invalidate(); + } + return true; + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + boolean needsInvalidate = false; + + final int overScrollMode = ViewCompat.getOverScrollMode(this); + if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS || + (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && + mAdapter != null && mAdapter.getCount() > 1)) { + if (!mLeftEdge.isFinished()) { + final int restoreCount = canvas.save(); + final int height = getHeight() - getPaddingTop() - getPaddingBottom(); + + canvas.rotate(270); + canvas.translate(-height + getPaddingTop(), 0); + mLeftEdge.setSize(height, getWidth()); + needsInvalidate |= mLeftEdge.draw(canvas); + canvas.restoreToCount(restoreCount); + } + if (!mRightEdge.isFinished()) { + final int restoreCount = canvas.save(); + final int width = getWidth(); + final int height = getHeight() - getPaddingTop() - getPaddingBottom(); + final int itemCount = mAdapter != null ? mAdapter.getCount() : 1; + + canvas.rotate(90); + canvas.translate(-getPaddingTop(), + -itemCount * (width + mPageMargin) + mPageMargin); + mRightEdge.setSize(height, width); + needsInvalidate |= mRightEdge.draw(canvas); + canvas.restoreToCount(restoreCount); + } + } else { + mLeftEdge.finish(); + mRightEdge.finish(); + } + + if (needsInvalidate) { + // Keep animating + invalidate(); + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + // Draw the margin drawable if needed. + if (mPageMargin > 0 && mMarginDrawable != null) { + final int scrollX = getScrollX(); + final int width = getWidth(); + final int offset = scrollX % (width + mPageMargin); + if (offset != 0) { + // Pages fit completely when settled; we only need to draw when in between + final int left = scrollX - offset + width; + mMarginDrawable.setBounds(left, 0, left + mPageMargin, getHeight()); + mMarginDrawable.draw(canvas); + } + } + } + + /** + * Start a fake drag of the pager. + * + *

A fake drag can be useful if you want to synchronize the motion of the ViewPager + * with the touch scrolling of another view, while still letting the ViewPager + * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.) + * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call + * {@link #endFakeDrag()} to complete the fake drag and fling as necessary. + * + *

During a fake drag the ViewPager will ignore all touch events. If a real drag + * is already in progress, this method will return false. + * + * @return true if the fake drag began successfully, false if it could not be started. + * + * @see #fakeDragBy(float) + * @see #endFakeDrag() + */ + public boolean beginFakeDrag() { + if (mIsBeingDragged) { + return false; + } + mFakeDragging = true; + setScrollState(SCROLL_STATE_DRAGGING); + mInitialMotionX = mLastMotionX = 0; + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } else { + mVelocityTracker.clear(); + } + final long time = SystemClock.uptimeMillis(); + final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0); + mVelocityTracker.addMovement(ev); + ev.recycle(); + mFakeDragBeginTime = time; + return true; + } + + /** + * End a fake drag of the pager. + * + * @see #beginFakeDrag() + * @see #fakeDragBy(float) + */ + public void endFakeDrag() { + if (!mFakeDragging) { + throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first."); + } + + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + int initialVelocity = (int)VelocityTrackerCompat.getYVelocity( + velocityTracker, mActivePointerId); + mPopulatePending = true; + if ((Math.abs(initialVelocity) > mMinimumVelocity) + || Math.abs(mInitialMotionX-mLastMotionX) >= (getWidth()/3)) { + if (mLastMotionX > mInitialMotionX) { + setCurrentItemInternal(mCurItem-1, true, true); + } else { + setCurrentItemInternal(mCurItem+1, true, true); + } + } else { + setCurrentItemInternal(mCurItem, true, true); + } + endDrag(); + + mFakeDragging = false; + } + + /** + * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first. + * + * @param xOffset Offset in pixels to drag by. + * @see #beginFakeDrag() + * @see #endFakeDrag() + */ + public void fakeDragBy(float xOffset) { + if (!mFakeDragging) { + throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first."); + } + + mLastMotionX += xOffset; + float scrollX = getScrollX() - xOffset; + final int width = getWidth(); + final int widthWithMargin = width + mPageMargin; + + final float leftBound = Math.max(0, (mCurItem - 1) * widthWithMargin); + final float rightBound = + Math.min(mCurItem + 1, mAdapter.getCount() - 1) * widthWithMargin; + if (scrollX < leftBound) { + scrollX = leftBound; + } else if (scrollX > rightBound) { + scrollX = rightBound; + } + // Don't lose the rounded component + mLastMotionX += scrollX - (int) scrollX; + scrollTo((int) scrollX, getScrollY()); + if (mOnPageChangeListener != null) { + final int position = (int) scrollX / widthWithMargin; + final int positionOffsetPixels = (int) scrollX % widthWithMargin; + final float positionOffset = (float) positionOffsetPixels / widthWithMargin; + mOnPageChangeListener.onPageScrolled(position, positionOffset, + positionOffsetPixels); + } + + // Synthesize an event for the VelocityTracker. + final long time = SystemClock.uptimeMillis(); + final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE, + mLastMotionX, 0, 0); + mVelocityTracker.addMovement(ev); + ev.recycle(); + } + + /** + * Returns true if a fake drag is in progress. + * + * @return true if currently in a fake drag, false otherwise. + * + * @see #beginFakeDrag() + * @see #fakeDragBy(float) + * @see #endFakeDrag() + */ + public boolean isFakeDragging() { + return mFakeDragging; + } + + private void onSecondaryPointerUp(MotionEvent ev) { + final int pointerIndex = MotionEventCompat.getActionIndex(ev); + final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); + if (pointerId == mActivePointerId) { + // This was our active pointer going up. Choose a new + // active pointer and adjust accordingly. + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; + mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex); + mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); + if (mVelocityTracker != null) { + mVelocityTracker.clear(); + } + } + } + + private void endDrag() { + mIsBeingDragged = false; + mIsUnableToDrag = false; + + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + + private void setScrollingCacheEnabled(boolean enabled) { + if (mScrollingCacheEnabled != enabled) { + mScrollingCacheEnabled = enabled; + if (USE_CACHE) { + final int size = getChildCount(); + for (int i = 0; i < size; ++i) { + final View child = getChildAt(i); + if (child.getVisibility() != GONE) { + child.setDrawingCacheEnabled(enabled); + } + } + } + } + } + + /** + * Tests scrollability within child views of v given a delta of dx. + * + * @param v View to test for horizontal scrollability + * @param checkV Whether the view v passed should itself be checked for scrollability (true), + * or just its children (false). + * @param dx Delta scrolled in pixels + * @param x X coordinate of the active touch point + * @param y Y coordinate of the active touch point + * @return true if child views of v can be scrolled by delta of dx. + */ + protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { + if (v instanceof ViewGroup) { + final ViewGroup group = (ViewGroup) v; + final int scrollX = v.getScrollX(); + final int scrollY = v.getScrollY(); + final int count = group.getChildCount(); + // Count backwards - let topmost views consume scroll distance first. + for (int i = count - 1; i >= 0; i--) { + // TODO: Add versioned support here for transformed views. + // This will not work for transformed views in Honeycomb+ + final View child = group.getChildAt(i); + if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() && + y + scrollY >= child.getTop() && y + scrollY < child.getBottom() && + canScroll(child, true, dx, x + scrollX - child.getLeft(), + y + scrollY - child.getTop())) { + return true; + } + } + } + + return checkV && ViewCompat.canScrollHorizontally(v, -dx); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + // Let the focused view and/or our descendants get the key first + return super.dispatchKeyEvent(event) || executeKeyEvent(event); + } + + /** + * You can call this function yourself to have the scroll view perform + * scrolling from a key event, just as if the event had been dispatched to + * it by the view hierarchy. + * + * @param event The key event to execute. + * @return Return true if the event was handled, else false. + */ + public boolean executeKeyEvent(KeyEvent event) { + boolean handled = false; + if (event.getAction() == KeyEvent.ACTION_DOWN) { + switch (event.getKeyCode()) { + case KeyEvent.KEYCODE_DPAD_LEFT: + handled = arrowScroll(FOCUS_LEFT); + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + handled = arrowScroll(FOCUS_RIGHT); + break; + case KeyEvent.KEYCODE_TAB: + if (KeyEventCompat.hasNoModifiers(event)) { + handled = arrowScroll(FOCUS_FORWARD); + } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) { + handled = arrowScroll(FOCUS_BACKWARD); + } + break; + } + } + return handled; + } + + public boolean arrowScroll(int direction) { + View currentFocused = findFocus(); + if (currentFocused == this) currentFocused = null; + + boolean handled = false; + + View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, + direction); + if (nextFocused != null && nextFocused != currentFocused) { + if (direction == View.FOCUS_LEFT) { + // If there is nothing to the left, or this is causing us to + // jump to the right, then what we really want to do is page left. + if (currentFocused != null && nextFocused.getLeft() >= currentFocused.getLeft()) { + handled = pageLeft(); + } else { + handled = nextFocused.requestFocus(); + } + } else if (direction == View.FOCUS_RIGHT) { + // If there is nothing to the right, or this is causing us to + // jump to the left, then what we really want to do is page right. + if (currentFocused != null && nextFocused.getLeft() <= currentFocused.getLeft()) { + handled = pageRight(); + } else { + handled = nextFocused.requestFocus(); + } + } + } else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) { + // Trying to move left and nothing there; try to page. + handled = pageLeft(); + } else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) { + // Trying to move right and nothing there; try to page. + handled = pageRight(); + } + if (handled) { + playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); + } + return handled; + } + + boolean pageLeft() { + if (mCurItem > 0) { + setCurrentItem(mCurItem-1, true); + return true; + } + return false; + } + + boolean pageRight() { + if (mAdapter != null && mCurItem < (mAdapter.getCount()-1)) { + setCurrentItem(mCurItem+1, true); + return true; + } + return false; + } + + /** + * We only want the current page that is being shown to be focusable. + */ + @Override + public void addFocusables(ArrayList views, int direction, int focusableMode) { + final int focusableCount = views.size(); + + final int descendantFocusability = getDescendantFocusability(); + + if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { + for (int i = 0; i < getChildCount(); i++) { + final View child = getChildAt(i); + if (child.getVisibility() == VISIBLE) { + ItemInfo ii = infoForChild(child); + if (ii != null && ii.position == mCurItem) { + child.addFocusables(views, direction, focusableMode); + } + } + } + } + + // we add ourselves (if focusable) in all cases except for when we are + // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is + // to avoid the focus search finding layouts when a more precise search + // among the focusable children would be more interesting. + if ( + descendantFocusability != FOCUS_AFTER_DESCENDANTS || + // No focusable descendants + (focusableCount == views.size())) { + // Note that we can't call the superclass here, because it will + // add all views in. So we need to do the same thing View does. + if (!isFocusable()) { + return; + } + if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE && + isInTouchMode() && !isFocusableInTouchMode()) { + return; + } + if (views != null) { + views.add(this); + } + } + } + + /** + * We only want the current page that is being shown to be touchable. + */ + @Override + public void addTouchables(ArrayList views) { + // Note that we don't call super.addTouchables(), which means that + // we don't call View.addTouchables(). This is okay because a ViewPager + // is itself not touchable. + for (int i = 0; i < getChildCount(); i++) { + final View child = getChildAt(i); + if (child.getVisibility() == VISIBLE) { + ItemInfo ii = infoForChild(child); + if (ii != null && ii.position == mCurItem) { + child.addTouchables(views); + } + } + } + } + + /** + * We only want the current page that is being shown to be focusable. + */ + @Override + protected boolean onRequestFocusInDescendants(int direction, + Rect previouslyFocusedRect) { + int index; + int increment; + int end; + int count = getChildCount(); + if ((direction & FOCUS_FORWARD) != 0) { + index = 0; + increment = 1; + end = count; + } else { + index = count - 1; + increment = -1; + end = -1; + } + for (int i = index; i != end; i += increment) { + View child = getChildAt(i); + if (child.getVisibility() == VISIBLE) { + ItemInfo ii = infoForChild(child); + if (ii != null && ii.position == mCurItem) { + if (child.requestFocus(direction, previouslyFocusedRect)) { + return true; + } + } + } + } + return false; + } + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + // ViewPagers should only report accessibility info for the current page, + // otherwise things get very confusing. + + // TODO: Should this note something about the paging container? + + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + if (child.getVisibility() == VISIBLE) { + final ItemInfo ii = infoForChild(child); + if (ii != null && ii.position == mCurItem && + child.dispatchPopulateAccessibilityEvent(event)) { + return true; + } + } + } + + return false; + } + + private class DataSetObserver implements PagerAdapter.DataSetObserver { + @Override + public void onDataSetChanged() { + dataSetChanged(); + } + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/Window.java b/actionbarsherlock/library/src/android/support/v4/view/Window.java new file mode 100644 index 000000000..531189574 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/Window.java @@ -0,0 +1,88 @@ +/* + * 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 android.support.v4.view; + +import android.content.Context; + +/** + *

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.

+ * + *

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.

+ */ +public abstract class Window extends android.view.Window { + /* + * We use long values so that we can intercept the call to + * requestWindowFeature in our Activity. + */ + + /** + * Flag for enabling the Action Bar. This is enabled by default for some + * devices. The Action Bar replaces the title bar and provides an alternate + * location for an on-screen menu button on some devices. + */ + public static final long FEATURE_ACTION_BAR = android.view.Window.FEATURE_ACTION_BAR; + + /** + * Enable the pre-3.0 action bar implementation to honor the 'withText' + * attribute on menu items regardless of display resolution and/or density. + */ + //Hopefully the native Window.FEATURE_XXX numbering won't get this high anytime soon. + public static final long FEATURE_ACTION_BAR_ITEM_TEXT = 31; + + /** + * Flag for requesting an Action Bar that overlays window content. Normally + * an Action Bar will sit in the space above window content, but if this + * feature is requested along with {@link #FEATURE_ACTION_BAR} it will be + * layered over the window content itself. This is useful if you would like + * your app to have more control over how the Action Bar is displayed, such + * as letting application content scroll beneath an Action Bar with a + * transparent background or otherwise displaying a transparent/translucent + * Action Bar over application content. + */ + public static final long FEATURE_ACTION_BAR_OVERLAY = android.view.Window.FEATURE_ACTION_BAR_OVERLAY; + + /** + * Flag for specifying the behavior of action modes when an Action Bar is + * not present. If overlay is enabled, the action mode UI will be allowed to + * cover existing window content. + */ + public static final long FEATURE_ACTION_MODE_OVERLAY = android.view.Window.FEATURE_ACTION_MODE_OVERLAY; + + /** + * Flag for indeterminate progress . + */ + public static final long FEATURE_INDETERMINATE_PROGRESS = android.view.Window.FEATURE_INDETERMINATE_PROGRESS; + + + + /** + * Create a new instance for a context. + * + * @param context Context. + */ + public Window(Context context) { + super(context); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityEventCompat.java b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityEventCompat.java new file mode 100644 index 000000000..40c3ff90e --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityEventCompat.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 android.support.v4.view.accessibility; + +//import android.os.Build; +import android.view.accessibility.AccessibilityEvent; + +/** + * Helper for accessing newer features in AccessibilityEvent. + */ +public class AccessibilityEventCompat { + + static interface AccessibilityEventVersionImpl { + public int getRecordCount(AccessibilityEvent event); + public void appendRecord(AccessibilityEvent event, Object record); + public Object getRecord(AccessibilityEvent event, int index); + } + + static class AccessibilityEventStubImpl implements AccessibilityEventVersionImpl { + + public void appendRecord(AccessibilityEvent event, Object record) { + + } + + public Object getRecord(AccessibilityEvent event, int index) { + return null; + } + + public int getRecordCount(AccessibilityEvent event) { + return 0; + } + } +/* + static class AccessibilityEventIcsImpl extends AccessibilityEventStubImpl { + + @Override + public void appendRecord(AccessibilityEvent event, Object record) { + AccessibilityEventCompatIcs.appendRecord(event, record); + } + + @Override + public Object getRecord(AccessibilityEvent event, int index) { + return AccessibilityEventCompatIcs.getRecord(event, index); + } + + @Override + public int getRecordCount(AccessibilityEvent event) { + return AccessibilityEventCompatIcs.getRecordCount(event); + } + } +*/ + private final static AccessibilityEventVersionImpl IMPL; + + static { + //if (Build.VERSION.SDK_INT >= 14) { // ICS + // IMPL = new AccessibilityEventIcsImpl(); + //} else { + IMPL = new AccessibilityEventStubImpl(); + //} + } + + /** + * Represents the event of a hover enter over a {@link android.view.View}. + */ + public static final int TYPE_VIEW_HOVER_ENTER = 0x00000080; + + /** + * Represents the event of a hover exit over a {@link android.view.View}. + */ + public static final int TYPE_VIEW_HOVER_EXIT = 0x00000100; + + /** + * Represents the event of starting a touch exploration gesture. + */ + public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200; + + /** + * Represents the event of ending a touch exploration gesture. + */ + public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400; + + /** + * Represents the event of changing the content of a window. + */ + public static final int TYPE_WINDOW_CONTENT_CHANGED = 0x00000800; + + /** + * Represents the event of scrolling a view. + */ + public static final int TYPE_VIEW_SCROLLED = 0x00001000; + + /** + * Represents the event of changing the selection in an {@link android.widget.EditText}. + */ + public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 0x00002000; + + /* + * Hide constructor from clients. + */ + private AccessibilityEventCompat() { + + } + + /** + * Gets the number of records contained in the event. + * + * @return The number of records. + */ + public static int getRecordCount(AccessibilityEvent event) { + return IMPL.getRecordCount(event); + } + + /** + * Appends an {@link android.view.accessibility.AccessibilityRecord} to the end of + * event records. + * + * @param record The record to append. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public static void appendRecord(AccessibilityEvent event, AccessibilityRecordCompat record) { + IMPL.appendRecord(event, record.getImpl()); + } + + /** + * Gets the record at a given index. + * + * @param index The index. + * @return The record at the specified index. + */ + public static AccessibilityRecordCompat getRecord(AccessibilityEvent event, int index) { + return new AccessibilityRecordCompat(IMPL.getRecord(event, index)); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java new file mode 100644 index 000000000..62e06124d --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.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 android.support.v4.view.accessibility; + +//import android.view.accessibility.AccessibilityEvent; +//import android.view.accessibility.AccessibilityRecord; + +/** + * ICS specific AccessibilityEvent API implementation. + */ +class AccessibilityEventCompatIcs { +/* + public static int getRecordCount(AccessibilityEvent event) { + return event.getRecordCount(); + } + + public static void appendRecord(AccessibilityEvent event, Object record) { + event.appendRecord((AccessibilityRecord) record); + } + + public static Object getRecord(AccessibilityEvent event, int index) { + return event.getRecord(index); + } +*/ +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityManagerCompat.java b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityManagerCompat.java new file mode 100644 index 000000000..2ce8c4cfd --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityManagerCompat.java @@ -0,0 +1,204 @@ +/* + * 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 android.support.v4.view.accessibility; + +import android.accessibilityservice.AccessibilityServiceInfo; +//import android.os.Build; +import android.view.accessibility.AccessibilityManager; +//import android.support.v4.view.accessibility.AccessibilityManagerCompatIcs.AccessibilityStateChangeListenerBridge; + +import java.util.List; + +/** + * Helper for accessing newer features in AccessibilityManager. + */ +public class AccessibilityManagerCompat { + + interface AccessibilityManagerVersionImpl { + public Object newAccessiblityStateChangeListener( + AccessibilityStateChangeListenerCompat listener); + public boolean addAccessibilityStateChangeListener(AccessibilityManager manager, + AccessibilityStateChangeListenerCompat listener); + public boolean removeAccessibilityStateChangeListener(AccessibilityManager manager, + AccessibilityStateChangeListenerCompat listener); + public List getEnabledAccessibilityServiceList( + AccessibilityManager manager,int feedbackTypeFlags); + public List getInstalledAccessibilityServiceList( + AccessibilityManager manager); + public boolean isTouchExplorationEnabled(AccessibilityManager manager); + } + + static class AccessibilityManagerStubImpl implements AccessibilityManagerVersionImpl { + public Object newAccessiblityStateChangeListener( + AccessibilityStateChangeListenerCompat listener) { + return null; + } + + public boolean addAccessibilityStateChangeListener(AccessibilityManager manager, + AccessibilityStateChangeListenerCompat listener) { + return false; + } + + public boolean removeAccessibilityStateChangeListener(AccessibilityManager manager, + AccessibilityStateChangeListenerCompat listener) { + return false; + } + + public List getEnabledAccessibilityServiceList( + AccessibilityManager manager, int feedbackTypeFlags) { + return null; + } + + public List getInstalledAccessibilityServiceList( + AccessibilityManager manager) { + return null; + } + + public boolean isTouchExplorationEnabled(AccessibilityManager manager) { + return false; + } + } +/* + static class AccessibilityManagerIcsImpl extends AccessibilityManagerStubImpl { + + @Override + public Object newAccessiblityStateChangeListener( + final AccessibilityStateChangeListenerCompat listener) { + return AccessibilityManagerCompatIcs.newAccessibilityStateChangeListener( + new AccessibilityStateChangeListenerBridge() { + public void onAccessibilityStateChanged(boolean enabled) { + listener.onAccessibilityStateChanged(enabled); + } + }); + } + + @Override + public boolean addAccessibilityStateChangeListener(AccessibilityManager manager, + AccessibilityStateChangeListenerCompat listener) { + return AccessibilityManagerCompatIcs.addAccessibilityStateChangeListener(manager, + listener.mListener); + } + + @Override + public boolean removeAccessibilityStateChangeListener(AccessibilityManager manager, + final AccessibilityStateChangeListenerCompat listener) { + return AccessibilityManagerCompatIcs.removeAccessibilityStateChangeListener(manager, + listener.mListener); + } + + @Override + public List getEnabledAccessibilityServiceList( + AccessibilityManager manager, int feedbackTypeFlags) { + return AccessibilityManagerCompatIcs.getEnabledAccessibilityServiceList(manager, + feedbackTypeFlags); + } + + @Override + public List getInstalledAccessibilityServiceList( + AccessibilityManager manager) { + return AccessibilityManagerCompatIcs.getInstalledAccessibilityServiceList(manager); + } + + @Override + public boolean isTouchExplorationEnabled(AccessibilityManager manager) { + return AccessibilityManagerCompatIcs.isTouchExplorationEnabled(manager); + } + } +*/ + static { + //if (Build.VERSION.SDK_INT >= 14) { // ICS + // IMPL = new AccessibilityManagerIcsImpl(); + //} else { + IMPL = new AccessibilityManagerStubImpl(); + //} + } + + private static final AccessibilityManagerVersionImpl IMPL; + + /** + * Registers an {@link AccessibilityManager.AccessibilityStateChangeListener} for changes in + * the global accessibility state of the system. + * + * @param manager The accessibility manager. + * @param listener The listener. + * @return True if successfully registered. + */ + public static boolean addAccessibilityStateChangeListener(AccessibilityManager manager, + AccessibilityStateChangeListenerCompat listener) { + return IMPL.addAccessibilityStateChangeListener(manager, listener); + } + + /** + * Unregisters an {@link AccessibilityManager.AccessibilityStateChangeListener}. + * + * @param manager The accessibility manager. + * @param listener The listener. + * @return True if successfully unregistered. + */ + public static boolean removeAccessibilityStateChangeListener(AccessibilityManager manager, + AccessibilityStateChangeListenerCompat listener) { + return IMPL.removeAccessibilityStateChangeListener(manager, listener); + } + + /** + * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services. + * + * @param manager The accessibility manager. + * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. + */ + public static List getInstalledAccessibilityServiceList( + AccessibilityManager manager) { + return IMPL.getInstalledAccessibilityServiceList(manager); + } + + /** + * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services + * for a given feedback type. + * + * @param manager The accessibility manager. + * @param feedbackTypeFlags The feedback type flags. + * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. + * + * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE + * @see AccessibilityServiceInfo#FEEDBACK_GENERIC + * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC + * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN + * @see AccessibilityServiceInfo#FEEDBACK_VISUAL + */ + public static List getEnabledAccessibilityServiceList( + AccessibilityManager manager, int feedbackTypeFlags) { + return IMPL.getEnabledAccessibilityServiceList(manager, feedbackTypeFlags); + } + + /** + * Listener for the accessibility state. + */ + public static abstract class AccessibilityStateChangeListenerCompat { + final Object mListener; + + public AccessibilityStateChangeListenerCompat() { + mListener = IMPL.newAccessiblityStateChangeListener(this); + } + + /** + * Called back on change in the accessibility state. + * + * @param enabled Whether accessibility is enabled. + */ + public abstract void onAccessibilityStateChanged(boolean enabled); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java new file mode 100644 index 000000000..e94fc5076 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java @@ -0,0 +1,70 @@ +/* + * 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 android.support.v4.view.accessibility; + +//import android.accessibilityservice.AccessibilityServiceInfo; +//import android.view.accessibility.AccessibilityManager; +//import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; + +//import java.util.List; + +/** + * ICS specific AccessibilityManager API implementation. + */ +class AccessibilityManagerCompatIcs { +/* + interface AccessibilityStateChangeListenerBridge { + public void onAccessibilityStateChanged(boolean enabled); + } + + public static Object newAccessibilityStateChangeListener( + final AccessibilityStateChangeListenerBridge bridge) { + return new AccessibilityStateChangeListener() { + @Override + public void onAccessibilityStateChanged(boolean enabled) { + bridge.onAccessibilityStateChanged(enabled); + } + }; + } + + public static boolean addAccessibilityStateChangeListener(AccessibilityManager manager, + Object listener) { + return manager.addAccessibilityStateChangeListener( + (AccessibilityStateChangeListener)listener); + } + + public static boolean removeAccessibilityStateChangeListener(AccessibilityManager manager, + Object listener) { + return manager.removeAccessibilityStateChangeListener( + (AccessibilityStateChangeListener)listener); + } + + public static List getEnabledAccessibilityServiceList( + AccessibilityManager manager,int feedbackTypeFlags) { + return manager.getEnabledAccessibilityServiceList(feedbackTypeFlags); + } + + public static List getInstalledAccessibilityServiceList( + AccessibilityManager manager) { + return manager.getInstalledAccessibilityServiceList(); + } + + public static boolean isTouchExplorationEnabled(AccessibilityManager manager) { + return manager.isTouchExplorationEnabled(); + } +*/ +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java new file mode 100644 index 000000000..229c0c5b9 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java @@ -0,0 +1,1166 @@ +/* + * 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 android.support.v4.view.accessibility; + +import android.graphics.Rect; +//import android.os.Build; +import android.view.View; + +import java.util.ArrayList; +import java.util.List; + +/** + * Helper for accessing AccessibilityNodeInfo from newer platform versions. + */ +public class AccessibilityNodeInfoCompat { + + static interface AccessibilityNodeInfoImpl { + public Object obtain(); + public Object obtain(View source); + public Object obtain(Object info); + public void setSource(Object info, View source); + public int getWindowId(Object info); + public int getChildCount(Object info); + public Object getChild(Object info, int index); + public void addChild(Object info, View child); + public int getActions(Object info); + public void addAction(Object info, int action); + public boolean performAction(Object info, int action); + public List findAccessibilityNodeInfosByText(Object info, String text); + public Object getParent(Object info); + public void setParent(Object info, View parent); + public void getBoundsInParent(Object info, Rect outBounds); + public void setBoundsInParent(Object info, Rect bounds); + public void getBoundsInScreen(Object info, Rect outBounds); + public void setBoundsInScreen(Object info, Rect bounds); + public boolean isCheckable(Object info); + public void setCheckable(Object info, boolean checkable); + public boolean isChecked(Object info); + public void setChecked(Object info, boolean checked); + public boolean isFocusable(Object info); + public void setFocusable(Object info, boolean focusable); + public boolean isFocused(Object info); + public void setFocused(Object info, boolean focused); + public boolean isSelected(Object info); + public void setSelected(Object info, boolean selected); + public boolean isClickable(Object info); + public void setClickable(Object info, boolean clickable); + public boolean isLongClickable(Object info); + public void setLongClickable(Object info, boolean longClickable); + public boolean isEnabled(Object info); + public void setEnabled(Object info, boolean enabled); + public boolean isPassword(Object info); + public void setPassword(Object info, boolean password); + public boolean isScrollable(Object info); + public void setScrollable(Object info, boolean scrollable); + public CharSequence getPackageName(Object info); + public void setPackageName(Object info, CharSequence packageName); + public CharSequence getClassName(Object info); + public void setClassName(Object info, CharSequence className); + public CharSequence getText(Object info); + public void setText(Object info, CharSequence text); + public CharSequence getContentDescription(Object info); + public void setContentDescription(Object info, CharSequence contentDescription); + public void recycle(Object info); + } + + static class AccessibilityNodeInfoStubImpl implements AccessibilityNodeInfoImpl { + public Object obtain() { + return null; + } + + public Object obtain(View source) { + return null; + } + + public Object obtain(Object info) { + return null; + } + + public void addAction(Object info, int action) { + + } + + public void addChild(Object info, View child) { + + } + + public List findAccessibilityNodeInfosByText(Object info, String text) { + return null; + } + + public int getActions(Object info) { + return 0; + } + + public void getBoundsInParent(Object info, Rect outBounds) { + + } + + public void getBoundsInScreen(Object info, Rect outBounds) { + + } + + public Object getChild(Object info, int index) { + return null; + } + + public int getChildCount(Object info) { + return 0; + } + + public CharSequence getClassName(Object info) { + return null; + } + + public CharSequence getContentDescription(Object info) { + return null; + } + + public CharSequence getPackageName(Object info) { + return null; + } + + public AccessibilityNodeInfoCompat getParent(Object info) { + return null; + } + + public CharSequence getText(Object info) { + return null; + } + + public int getWindowId(Object info) { + return 0; + } + + public boolean isCheckable(Object info) { + return false; + } + + public boolean isChecked(Object info) { + return false; + } + + public boolean isClickable(Object info) { + return false; + } + + public boolean isEnabled(Object info) { + return false; + } + + public boolean isFocusable(Object info) { + return false; + } + + public boolean isFocused(Object info) { + return false; + } + + public boolean isLongClickable(Object info) { + return false; + } + + public boolean isPassword(Object info) { + return false; + } + + public boolean isScrollable(Object info) { + return false; + } + + public boolean isSelected(Object info) { + return false; + } + + public boolean performAction(Object info, int action) { + return false; + } + + public void setBoundsInParent(Object info, Rect bounds) { + + } + + public void setBoundsInScreen(Object info, Rect bounds) { + + } + + public void setCheckable(Object info, boolean checkable) { + + } + + public void setChecked(Object info, boolean checked) { + + } + + public void setClassName(Object info, CharSequence className) { + + } + + public void setClickable(Object info, boolean clickable) { + + } + + public void setContentDescription(Object info, CharSequence contentDescription) { + + } + + public void setEnabled(Object info, boolean enabled) { + + } + + public void setFocusable(Object info, boolean focusable) { + + } + + public void setFocused(Object info, boolean focused) { + + } + + public void setLongClickable(Object info, boolean longClickable) { + + } + + public void setPackageName(Object info, CharSequence packageName) { + + } + + public void setParent(Object info, View parent) { + + } + + public void setPassword(Object info, boolean password) { + + } + + public void setScrollable(Object info, boolean scrollable) { + + } + + public void setSelected(Object info, boolean selected) { + + } + + public void setSource(Object info, View source) { + + } + + public void setText(Object info, CharSequence text) { + + } + + public void recycle(Object info) { + + } + } +/* + static class AccessibilityNodeInfoIcsImpl extends AccessibilityNodeInfoStubImpl { + @Override + public Object obtain() { + return AccessibilityNodeInfoCompatIcs.obtain(); + } + + @Override + public Object obtain(View source) { + return AccessibilityNodeInfoCompatIcs.obtain(source); + } + + @Override + public Object obtain(Object info) { + return AccessibilityNodeInfoCompatIcs.obtain(info); + } + + @Override + public void addAction(Object info, int action) { + AccessibilityNodeInfoCompatIcs.addAction(info, action); + } + + @Override + public void addChild(Object info, View child) { + AccessibilityNodeInfoCompatIcs.addChild(info, child); + } + + @Override + public List findAccessibilityNodeInfosByText(Object info, String text) { + return AccessibilityNodeInfoCompatIcs.findAccessibilityNodeInfosByText(info, text); + } + + @Override + public int getActions(Object info) { + return AccessibilityNodeInfoCompatIcs.getActions(info); + } + + @Override + public void getBoundsInParent(Object info, Rect outBounds) { + AccessibilityNodeInfoCompatIcs.getBoundsInParent(info, outBounds); + } + + @Override + public void getBoundsInScreen(Object info, Rect outBounds) { + AccessibilityNodeInfoCompatIcs.getBoundsInScreen(info, outBounds); + } + + @Override + public AccessibilityNodeInfoCompat getChild(Object info, int index) { + return new AccessibilityNodeInfoCompat(IMPL.getChild(info, index)); + } + + @Override + public int getChildCount(Object info) { + return AccessibilityNodeInfoCompatIcs.getChildCount(info); + } + + @Override + public CharSequence getClassName(Object info) { + return AccessibilityNodeInfoCompatIcs.getClassName(info); + } + + @Override + public CharSequence getContentDescription(Object info) { + return AccessibilityNodeInfoCompatIcs.getContentDescription(info); + } + + @Override + public CharSequence getPackageName(Object info) { + return AccessibilityNodeInfoCompatIcs.getPackageName(info); + } + + @Override + public AccessibilityNodeInfoCompat getParent(Object info) { + return new AccessibilityNodeInfoCompat(IMPL.getParent(info)); + } + + @Override + public CharSequence getText(Object info) { + return AccessibilityNodeInfoCompatIcs.getText(info); + } + + @Override + public int getWindowId(Object info) { + return AccessibilityNodeInfoCompatIcs.getWindowId(info); + } + + @Override + public boolean isCheckable(Object info) { + return AccessibilityNodeInfoCompatIcs.isCheckable(info); + } + + @Override + public boolean isChecked(Object info) { + return AccessibilityNodeInfoCompatIcs.isChecked(info); + } + + @Override + public boolean isClickable(Object info) { + return AccessibilityNodeInfoCompatIcs.isClickable(info); + } + + @Override + public boolean isEnabled(Object info) { + return AccessibilityNodeInfoCompatIcs.isEnabled(info); + } + + @Override + public boolean isFocusable(Object info) { + return AccessibilityNodeInfoCompatIcs.isFocusable(info); + } + + @Override + public boolean isFocused(Object info) { + return AccessibilityNodeInfoCompatIcs.isFocused(info); + } + + @Override + public boolean isLongClickable(Object info) { + return AccessibilityNodeInfoCompatIcs.isLongClickable(info); + } + + @Override + public boolean isPassword(Object info) { + return AccessibilityNodeInfoCompatIcs.isPassword(info); + } + + @Override + public boolean isScrollable(Object info) { + return AccessibilityNodeInfoCompatIcs.isScrollable(info); + } + + @Override + public boolean isSelected(Object info) { + return AccessibilityNodeInfoCompatIcs.isSelected(info); + } + + @Override + public boolean performAction(Object info, int action) { + return AccessibilityNodeInfoCompatIcs.performAction(info, action); + } + + @Override + public void setBoundsInParent(Object info, Rect bounds) { + AccessibilityNodeInfoCompatIcs.setBoundsInParent(info, bounds); + } + + @Override + public void setBoundsInScreen(Object info, Rect bounds) { + AccessibilityNodeInfoCompatIcs.setBoundsInScreen(info, bounds); + } + + @Override + public void setCheckable(Object info, boolean checkable) { + AccessibilityNodeInfoCompatIcs.setCheckable(info, checkable); + } + + @Override + public void setChecked(Object info, boolean checked) { + AccessibilityNodeInfoCompatIcs.setChecked(info, checked); + } + + @Override + public void setClassName(Object info, CharSequence className) { + AccessibilityNodeInfoCompatIcs.setClassName(info, className); + } + + @Override + public void setClickable(Object info, boolean clickable) { + AccessibilityNodeInfoCompatIcs.setClickable(info, clickable); + } + + @Override + public void setContentDescription(Object info, CharSequence contentDescription) { + AccessibilityNodeInfoCompatIcs.setContentDescription(info, contentDescription); + } + + @Override + public void setEnabled(Object info, boolean enabled) { + AccessibilityNodeInfoCompatIcs.setEnabled(info, enabled); + } + + @Override + public void setFocusable(Object info, boolean focusable) { + AccessibilityNodeInfoCompatIcs.setFocusable(info, focusable); + } + + @Override + public void setFocused(Object info, boolean focused) { + AccessibilityNodeInfoCompatIcs.setFocused(info, focused); + } + + @Override + public void setLongClickable(Object info, boolean longClickable) { + AccessibilityNodeInfoCompatIcs.setLongClickable(info, longClickable); + } + + @Override + public void setPackageName(Object info, CharSequence packageName) { + AccessibilityNodeInfoCompatIcs.setPackageName(info, packageName); + } + + @Override + public void setParent(Object info, View parent) { + AccessibilityNodeInfoCompatIcs.setParent(info, parent); + } + + @Override + public void setPassword(Object info, boolean password) { + AccessibilityNodeInfoCompatIcs.setPassword(info, password); + } + + @Override + public void setScrollable(Object info, boolean scrollable) { + AccessibilityNodeInfoCompatIcs.setScrollable(info, scrollable); + } + + @Override + public void setSelected(Object info, boolean selected) { + AccessibilityNodeInfoCompatIcs.setSelected(info, selected); + } + + @Override + public void setSource(Object info, View source) { + AccessibilityNodeInfoCompatIcs.setSource(info, source); + } + + @Override + public void setText(Object info, CharSequence text) { + AccessibilityNodeInfoCompatIcs.setText(info, text); + } + + @Override + public void recycle(Object info) { + AccessibilityNodeInfoCompatIcs.recycle(info); + } + } +*/ + static { + //if (Build.VERSION.SDK_INT >= 14) { // ICS + // IMPL = new AccessibilityNodeInfoIcsImpl(); + //} else { + IMPL = new AccessibilityNodeInfoStubImpl(); + //} + } + + private static final AccessibilityNodeInfoImpl IMPL; + + private final Object mInfo; + + // Actions. + + /** + * Action that focuses the node. + */ + public static final int ACTION_FOCUS = 0x00000001; + + /** + * Action that unfocuses the node. + */ + public static final int ACTION_CLEAR_FOCUS = 0x00000002; + + /** + * Action that selects the node. + */ + public static final int ACTION_SELECT = 0x00000004; + + /** + * Action that unselects the node. + */ + public static final int ACTION_CLEAR_SELECTION = 0x00000008; + + /* + * Hide constructor from clients. + */ + public AccessibilityNodeInfoCompat(Object info) { + mInfo = info; + } + + /** + * @return The wrapped actual implementation. + * + * @hide + */ + public Object getImpl() { + return mInfo; + } + + /** + * Returns a cached instance if such is available otherwise a new one and + * sets the source. + * + * @return An instance. + * @see #setSource(View) + */ + public static AccessibilityNodeInfoCompat obtain(View source) { + return new AccessibilityNodeInfoCompat(IMPL.obtain(source)); + } + + /** + * Returns a cached instance if such is available otherwise a new one. + * + * @return An instance. + */ + public static AccessibilityNodeInfoCompat obtain() { + return new AccessibilityNodeInfoCompat(IMPL.obtain()); + } + + /** + * Returns a cached instance if such is available or a new one is create. + * The returned instance is initialized from the given info. + * + * @param info The other info. + * @return An instance. + */ + public static AccessibilityNodeInfoCompat obtain(AccessibilityNodeInfoCompat info) { + return new AccessibilityNodeInfoCompat(IMPL.obtain(info.mInfo)); + } + + /** + * Sets the source. + * + * @param source The info source. + */ + public void setSource(View source) { + IMPL.setSource(mInfo, source); + } + + /** + * Gets the id of the window from which the info comes from. + * + * @return The window id. + */ + public int getWindowId() { + return IMPL.getWindowId(mInfo); + } + + /** + * Gets the number of children. + * + * @return The child count. + */ + public int getChildCount() { + return IMPL.getChildCount(mInfo); + } + + /** + * Get the child at given index. + *

+ * Note: It is a client responsibility to recycle the + * received info by calling {@link AccessibilityNodeInfoCompat#recycle()} to + * avoid creating of multiple instances. + *

+ * + * @param index The child index. + * @return The child node. + * @throws IllegalStateException If called outside of an + * AccessibilityService. + */ + public AccessibilityNodeInfoCompat getChild(int index) { + return new AccessibilityNodeInfoCompat(IMPL.getChild(mInfo, index)); + } + + /** + * Adds a child. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param child The child. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void addChild(View child) { + IMPL.addChild(mInfo, child); + } + + /** + * Gets the actions that can be performed on the node. + * + * @return The bit mask of with actions. + * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_FOCUS + * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_FOCUS + * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_SELECT + * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_SELECTION + */ + public int getActions() { + return IMPL.getActions(mInfo); + } + + /** + * Adds an action that can be performed on the node. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param action The action. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void addAction(int action) { + IMPL.addAction(mInfo, action); + } + + /** + * Performs an action on the node. + *

+ * Note: An action can be performed only if the request is + * made from an {@link android.accessibilityservice.AccessibilityService}. + *

+ * + * @param action The action to perform. + * @return True if the action was performed. + * @throws IllegalStateException If called outside of an + * AccessibilityService. + */ + public boolean performAction(int action) { + return IMPL.performAction(mInfo, action); + } + + /** + * Finds {@link android.view.accessibility.AccessibilityNodeInfo}s by text. The match + * is case insensitive containment. The search is relative to this info i.e. this + * info is the root of the traversed tree. + *

+ * Note: It is a client responsibility to recycle the + * received info by calling {@link android.view.accessibility.AccessibilityNodeInfo#recycle()} + * to avoid creating of multiple instances. + *

+ * + * @param text The searched text. + * @return A list of node info. + */ + public List findAccessibilityNodeInfosByText(String text) { + List result = new ArrayList(); + List infos = IMPL.findAccessibilityNodeInfosByText(mInfo, text); + final int infoCount = infos.size(); + for (int i = 0; i < infoCount; i++) { + Object info = infos.get(i); + result.add(new AccessibilityNodeInfoCompat(info)); + } + return result; + } + + /** + * Gets the parent. + *

+ * Note: It is a client responsibility to recycle the + * received info by calling {@link android.view.accessibility.AccessibilityNodeInfo#recycle()} + * to avoid creating of multiple instances. + *

+ * + * @return The parent. + */ + public AccessibilityNodeInfoCompat getParent() { + return new AccessibilityNodeInfoCompat(IMPL.getParent(mInfo)); + } + + /** + * Sets the parent. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param parent The parent. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setParent(View parent) { + IMPL.setParent(mInfo, parent); + } + + /** + * Gets the node bounds in parent coordinates. + * + * @param outBounds The output node bounds. + */ + public void getBoundsInParent(Rect outBounds) { + IMPL.getBoundsInParent(mInfo, outBounds); + } + + /** + * Sets the node bounds in parent coordinates. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param bounds The node bounds. + *@throws IllegalStateException If called from an AccessibilityService. + */ + public void setBoundsInParent(Rect bounds) { + IMPL.setBoundsInParent(mInfo, bounds); + } + + /** + * Gets the node bounds in screen coordinates. + * + * @param outBounds The output node bounds. + */ + public void getBoundsInScreen(Rect outBounds) { + IMPL.getBoundsInScreen(mInfo, outBounds); + } + + /** + * Sets the node bounds in screen coordinates. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param bounds The node bounds. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setBoundsInScreen(Rect bounds) { + IMPL.setBoundsInParent(mInfo, bounds); + } + + /** + * Gets whether this node is checkable. + * + * @return True if the node is checkable. + */ + public boolean isCheckable() { + return IMPL.isCheckable(mInfo); + } + + /** + * Sets whether this node is checkable. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param checkable True if the node is checkable. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setCheckable(boolean checkable) { + IMPL.setCheckable(mInfo, checkable); + } + + /** + * Gets whether this node is checked. + * + * @return True if the node is checked. + */ + public boolean isChecked() { + return IMPL.isChecked(mInfo); + } + + /** + * Sets whether this node is checked. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param checked True if the node is checked. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setChecked(boolean checked) { + IMPL.setChecked(mInfo, checked); + } + + /** + * Gets whether this node is focusable. + * + * @return True if the node is focusable. + */ + public boolean isFocusable() { + return IMPL.isFocusable(mInfo); + } + + /** + * Sets whether this node is focusable. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param focusable True if the node is focusable. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setFocusable(boolean focusable) { + IMPL.setFocusable(mInfo, focusable); + } + + /** + * Gets whether this node is focused. + * + * @return True if the node is focused. + */ + public boolean isFocused() { + return IMPL.isFocused(mInfo); + } + + /** + * Sets whether this node is focused. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param focused True if the node is focused. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setFocused(boolean focused) { + IMPL.setFocused(mInfo, focused); + } + + /** + * Gets whether this node is selected. + * + * @return True if the node is selected. + */ + public boolean isSelected() { + return IMPL.isSelected(mInfo); + } + + /** + * Sets whether this node is selected. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param selected True if the node is selected. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setSelected(boolean selected) { + IMPL.setSelected(mInfo, selected); + } + + /** + * Gets whether this node is clickable. + * + * @return True if the node is clickable. + */ + public boolean isClickable() { + return IMPL.isClickable(mInfo); + } + + /** + * Sets whether this node is clickable. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param clickable True if the node is clickable. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setClickable(boolean clickable) { + IMPL.setClickable(mInfo, clickable); + } + + /** + * Gets whether this node is long clickable. + * + * @return True if the node is long clickable. + */ + public boolean isLongClickable() { + return IMPL.isLongClickable(mInfo); + } + + /** + * Sets whether this node is long clickable. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param longClickable True if the node is long clickable. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setLongClickable(boolean longClickable) { + IMPL.setLongClickable(mInfo, longClickable); + } + + /** + * Gets whether this node is enabled. + * + * @return True if the node is enabled. + */ + public boolean isEnabled() { + return IMPL.isEnabled(mInfo); + } + + /** + * Sets whether this node is enabled. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param enabled True if the node is enabled. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setEnabled(boolean enabled) { + IMPL.setEnabled(mInfo, enabled); + } + + /** + * Gets whether this node is a password. + * + * @return True if the node is a password. + */ + public boolean isPassword() { + return IMPL.isPassword(mInfo); + } + + /** + * Sets whether this node is a password. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param password True if the node is a password. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setPassword(boolean password) { + IMPL.setPassword(mInfo, password); + } + + /** + * Gets if the node is scrollable. + * + * @return True if the node is scrollable, false otherwise. + */ + public boolean isScrollable() { + return IMPL.isScrollable(mInfo); + } + + /** + * Sets if the node is scrollable. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param scrollable True if the node is scrollable, false otherwise. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setScrollable(boolean scrollable) { + IMPL.setScrollable(mInfo, scrollable); + } + + /** + * Gets the package this node comes from. + * + * @return The package name. + */ + public CharSequence getPackageName() { + return IMPL.getPackageName(mInfo); + } + + /** + * Sets the package this node comes from. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param packageName The package name. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setPackageName(CharSequence packageName) { + IMPL.setPackageName(mInfo, packageName); + } + + /** + * Gets the class this node comes from. + * + * @return The class name. + */ + public CharSequence getClassName() { + return IMPL.getClassName(mInfo); + } + + /** + * Sets the class this node comes from. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param className The class name. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setClassName(CharSequence className) { + IMPL.setClassName(mInfo, className); + } + + /** + * Gets the text of this node. + * + * @return The text. + */ + public CharSequence getText() { + return IMPL.getText(mInfo); + } + + /** + * Sets the text of this node. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param text The text. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setText(CharSequence text) { + IMPL.setText(mInfo, text); + } + + /** + * Gets the content description of this node. + * + * @return The content description. + */ + public CharSequence getContentDescription() { + return IMPL.getContentDescription(mInfo); + } + + /** + * Sets the content description of this node. + *

+ * Note: Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. This class is + * made immutable before being delivered to an AccessibilityService. + *

+ * + * @param contentDescription The content description. + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setContentDescription(CharSequence contentDescription) { + IMPL.setContentDescription(mInfo, contentDescription); + } + + /** + * Return an instance back to be reused. + *

+ * Note: You must not touch the object after calling this function. + * + * @throws IllegalStateException If the info is already recycled. + */ + public void recycle() { + IMPL.recycle(mInfo); + } + + @Override + public int hashCode() { + return (mInfo == null) ? 0 : mInfo.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + AccessibilityNodeInfoCompat other = (AccessibilityNodeInfoCompat) obj; + if (mInfo == null) { + if (other.mInfo != null) { + return false; + } + } else if (!mInfo.equals(other.mInfo)) { + return false; + } + return true; + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java new file mode 100644 index 000000000..24a504fa4 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java @@ -0,0 +1,220 @@ +/* + * 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 android.support.v4.view.accessibility; + +//import android.graphics.Rect; +//import android.view.View; +//import android.view.accessibility.AccessibilityNodeInfo; + +//import java.util.List; + +/** + * ICS specific AccessibilityNodeInfo API implementation. + */ +class AccessibilityNodeInfoCompatIcs { +/* + public static Object obtain() { + return AccessibilityNodeInfo.obtain(); + } + + public static Object obtain(View source) { + return AccessibilityNodeInfo.obtain(source); + } + + public static Object obtain(Object info) { + return AccessibilityNodeInfo.obtain((AccessibilityNodeInfo) info); + } + + public static void addAction(Object info, int action) { + ((AccessibilityNodeInfo) info).addAction(action); + } + + public static void addChild(Object info, View child) { + ((AccessibilityNodeInfo) info).addChild(child); + } + + @SuppressWarnings("unchecked") + public static List findAccessibilityNodeInfosByText(Object info, String text) { + Object result = ((AccessibilityNodeInfo) info).findAccessibilityNodeInfosByText(text); + return (List) result; + } + + public static int getActions(Object info) { + return ((AccessibilityNodeInfo) info).getActions(); + } + + public static void getBoundsInParent(Object info, Rect outBounds) { + ((AccessibilityNodeInfo) info).getBoundsInParent(outBounds); + } + + public static void getBoundsInScreen(Object info, Rect outBounds) { + ((AccessibilityNodeInfo) info).getBoundsInScreen(outBounds); + } + + public static Object getChild(Object info, int index) { + return ((AccessibilityNodeInfo) info).getChild(index); + } + + public static int getChildCount(Object info) { + return ((AccessibilityNodeInfo) info).getChildCount(); + } + + public static CharSequence getClassName(Object info) { + return ((AccessibilityNodeInfo) info).getClassName(); + } + + public static CharSequence getContentDescription(Object info) { + return ((AccessibilityNodeInfo) info).getContentDescription(); + } + + public static CharSequence getPackageName(Object info) { + return ((AccessibilityNodeInfo) info).getPackageName(); + } + + public static Object getParent(Object info) { + return ((AccessibilityNodeInfo) info).getParent(); + } + + public static CharSequence getText(Object info) { + return ((AccessibilityNodeInfo) info).getText(); + } + + public static int getWindowId(Object info) { + return ((AccessibilityNodeInfo) info).getWindowId(); + } + + public static boolean isCheckable(Object info) { + return ((AccessibilityNodeInfo) info).isCheckable(); + } + + public static boolean isChecked(Object info) { + return ((AccessibilityNodeInfo) info).isChecked(); + } + + public static boolean isClickable(Object info) { + return ((AccessibilityNodeInfo) info).isClickable(); + } + + public static boolean isEnabled(Object info) { + return ((AccessibilityNodeInfo) info).isEnabled(); + } + + public static boolean isFocusable(Object info) { + return ((AccessibilityNodeInfo) info).isFocusable(); + } + + public static boolean isFocused(Object info) { + return ((AccessibilityNodeInfo) info).isFocused(); + } + + public static boolean isLongClickable(Object info) { + return ((AccessibilityNodeInfo) info).isLongClickable(); + } + + public static boolean isPassword(Object info) { + return ((AccessibilityNodeInfo) info).isPassword(); + } + + public static boolean isScrollable(Object info) { + return ((AccessibilityNodeInfo) info).isScrollable(); + } + + public static boolean isSelected(Object info) { + return ((AccessibilityNodeInfo) info).isSelected(); + } + + public static boolean performAction(Object info, int action) { + return ((AccessibilityNodeInfo) info).performAction(action); + } + + public static void setBoundsInParent(Object info, Rect bounds) { + ((AccessibilityNodeInfo) info).setBoundsInParent(bounds); + } + + public static void setBoundsInScreen(Object info, Rect bounds) { + ((AccessibilityNodeInfo) info).setBoundsInScreen(bounds); + } + + public static void setCheckable(Object info, boolean checkable) { + ((AccessibilityNodeInfo) info).setCheckable(checkable); + } + + public static void setChecked(Object info, boolean checked) { + ((AccessibilityNodeInfo) info).setChecked(checked); + } + + public static void setClassName(Object info, CharSequence className) { + ((AccessibilityNodeInfo) info).setClassName(className); + } + + public static void setClickable(Object info, boolean clickable) { + ((AccessibilityNodeInfo) info).setClickable(clickable); + } + + public static void setContentDescription(Object info, CharSequence contentDescription) { + ((AccessibilityNodeInfo) info).setContentDescription(contentDescription); + } + + public static void setEnabled(Object info, boolean enabled) { + ((AccessibilityNodeInfo) info).setEnabled(enabled); + } + + public static void setFocusable(Object info, boolean focusable) { + ((AccessibilityNodeInfo) info).setFocusable(focusable); + } + + public static void setFocused(Object info, boolean focused) { + ((AccessibilityNodeInfo) info).setFocused(focused); + } + + public static void setLongClickable(Object info, boolean longClickable) { + ((AccessibilityNodeInfo) info).setLongClickable(longClickable); + } + + public static void setPackageName(Object info, CharSequence packageName) { + ((AccessibilityNodeInfo) info).setPackageName(packageName); + } + + public static void setParent(Object info, View parent) { + ((AccessibilityNodeInfo) info).setParent(parent); + } + + public static void setPassword(Object info, boolean password) { + ((AccessibilityNodeInfo) info).setPassword(password); + } + + public static void setScrollable(Object info, boolean scrollable) { + ((AccessibilityNodeInfo) info).setScrollable(scrollable); + } + + public static void setSelected(Object info, boolean selected) { + ((AccessibilityNodeInfo) info).setSelected(selected); + } + + public static void setSource(Object info, View source) { + ((AccessibilityNodeInfo) info).setSource(source); + } + + public static void setText(Object info, CharSequence text) { + ((AccessibilityNodeInfo) info).setText(text); + } + + public static void recycle(Object info) { + ((AccessibilityNodeInfo) info).recycle(); + } +*/ +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityRecordCompat.java b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityRecordCompat.java new file mode 100644 index 000000000..5967b8b85 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityRecordCompat.java @@ -0,0 +1,957 @@ +/* + * 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 android.support.v4.view.accessibility; + +//import android.os.Build; +import android.os.Parcelable; +import android.view.View; + +import java.util.List; + +/** + * Helper for accessing AccessibilityRecord from newer platform versions. + */ +public class AccessibilityRecordCompat { + + static interface AccessibilityRecordImpl { + public Object obtain(); + public Object obtain(Object record); + public void setSource(Object record, View source); + public Object getSource(Object record); + public int getWindowId(Object record); + public boolean isChecked(Object record); + public void setChecked(Object record, boolean isChecked); + public boolean isEnabled(Object record); + public void setEnabled(Object record, boolean isEnabled); + public boolean isPassword(Object record); + public void setPassword(Object record, boolean isPassword); + public boolean isFullScreen(Object record); + public void setFullScreen(Object record, boolean isFullScreen); + public boolean isScrollable(Object record); + public void setScrollable(Object record, boolean scrollable); + public int getItemCount(Object record); + public void setItemCount(Object record, int itemCount); + public int getCurrentItemIndex(Object record); + public void setCurrentItemIndex(Object record, int currentItemIndex); + public int getFromIndex(Object record); + public void setFromIndex(Object record, int fromIndex); + public int getToIndex(Object record); + public void setToIndex(Object record, int toIndex); + public int getScrollX(Object record); + public void setScrollX(Object record, int scrollX); + public int getScrollY(Object record); + public void setScrollY(Object record, int scrollY); + public int getMaxScrollX(Object record); + public void setMaxScrollX(Object record, int maxScrollX); + public int getMaxScrollY(Object record); + public void setMaxScrollY(Object record, int maxScrollY); + public int getAddedCount(Object record); + public void setAddedCount(Object record, int addedCount); + public int getRemovedCount(Object record); + public void setRemovedCount(Object record, int removedCount); + public CharSequence getClassName(Object record); + public void setClassName(Object record, CharSequence className); + public List getText(Object record); + public CharSequence getBeforeText(Object record); + public void setBeforeText(Object record, CharSequence beforeText); + public CharSequence getContentDescription(Object record); + public void setContentDescription(Object record, CharSequence contentDescription); + public Parcelable getParcelableData(Object record); + public void setParcelableData(Object record, Parcelable parcelableData); + public void recycle(Object record); + } + + static class AccessibilityRecordStubImpl implements AccessibilityRecordImpl { + public Object obtain() { + return null; + } + + public Object obtain(Object record) { + return null; + } + + public int getAddedCount(Object record) { + return 0; + } + + public CharSequence getBeforeText(Object record) { + return null; + } + + public CharSequence getClassName(Object record) { + return null; + } + + public CharSequence getContentDescription(Object record) { + return null; + } + + public int getCurrentItemIndex(Object record) { + return 0; + } + + public int getFromIndex(Object record) { + return 0; + } + + public int getItemCount(Object record) { + return 0; + } + + public int getMaxScrollX(Object record) { + return 0; + } + + public int getMaxScrollY(Object record) { + return 0; + } + + public Parcelable getParcelableData(Object record) { + return null; + } + + public int getRemovedCount(Object record) { + return 0; + } + + public int getScrollX(Object record) { + return 0; + } + + public int getScrollY(Object record) { + return 0; + } + + public Object getSource(Object record) { + return null; + } + + public List getText(Object record) { + return null; + } + + public int getToIndex(Object record) { + return 0; + } + + public int getWindowId(Object record) { + return 0; + } + + public boolean isChecked(Object record) { + return false; + } + + public boolean isEnabled(Object record) { + return false; + } + + public boolean isFullScreen(Object record) { + return false; + } + + public boolean isPassword(Object record) { + return false; + } + + public boolean isScrollable(Object record) { + return false; + } + + public void recycle(Object record) { + + } + + public void setAddedCount(Object record, int addedCount) { + + } + + public void setBeforeText(Object record, CharSequence beforeText) { + + } + + public void setChecked(Object record, boolean isChecked) { + + } + + public void setClassName(Object record, CharSequence className) { + + } + + public void setContentDescription(Object record, CharSequence contentDescription) { + + } + + public void setCurrentItemIndex(Object record, int currentItemIndex) { + + } + + public void setEnabled(Object record, boolean isEnabled) { + + } + + public void setFromIndex(Object record, int fromIndex) { + + } + + public void setFullScreen(Object record, boolean isFullScreen) { + + } + + public void setItemCount(Object record, int itemCount) { + + } + + public void setMaxScrollX(Object record, int maxScrollX) { + + } + + public void setMaxScrollY(Object record, int maxScrollY) { + + } + + public void setParcelableData(Object record, Parcelable parcelableData) { + + } + + public void setPassword(Object record, boolean isPassword) { + + } + + public void setRemovedCount(Object record, int removedCount) { + + } + + public void setScrollX(Object record, int scrollX) { + + } + + public void setScrollY(Object record, int scrollY) { + + } + + public void setScrollable(Object record, boolean scrollable) { + + } + + public void setSource(Object record, View source) { + + } + + public void setToIndex(Object record, int toIndex) { + + } + } +/* + static class AccessibilityRecordIcsImpl implements AccessibilityRecordImpl { + public Object obtain() { + return AccessibilityRecordCompatIcs.obtain(); + } + + public Object obtain(Object record) { + return AccessibilityRecordCompatIcs.obtain(record); + } + + public int getAddedCount(Object record) { + return AccessibilityRecordCompatIcs.getAddedCount(record); + } + + public CharSequence getBeforeText(Object record) { + return AccessibilityRecordCompatIcs.getBeforeText(record); + } + + public CharSequence getClassName(Object record) { + return AccessibilityRecordCompatIcs.getClassName(record); + } + + public CharSequence getContentDescription(Object record) { + return AccessibilityRecordCompatIcs.getContentDescription(record); + } + + public int getCurrentItemIndex(Object record) { + return AccessibilityRecordCompatIcs.getCurrentItemIndex(record); + } + + public int getFromIndex(Object record) { + return AccessibilityRecordCompatIcs.getFromIndex(record); + } + + public int getItemCount(Object record) { + return AccessibilityRecordCompatIcs.getItemCount(record); + } + + public int getMaxScrollX(Object record) { + // TODO: Uncomment when these APIs become public + return 0;//AccessibilityRecordCompatIcs.getMaxScrollX(record); + } + + public int getMaxScrollY(Object record) { + // TODO: Uncomment when these APIs become public + return 0;//AccessibilityRecordCompatIcs.getMaxScrollY(record); + } + + public Parcelable getParcelableData(Object record) { + return AccessibilityRecordCompatIcs.getParcelableData(record); + } + + public int getRemovedCount(Object record) { + return AccessibilityRecordCompatIcs.getRemovedCount(record); + } + + public int getScrollX(Object record) { + return AccessibilityRecordCompatIcs.getScrollX(record); + } + + public int getScrollY(Object record) { + return AccessibilityRecordCompatIcs.getScrollY(record); + } + + public Object getSource(Object record) { + return AccessibilityRecordCompatIcs.getSource(record); + } + + public List getText(Object record) { + return AccessibilityRecordCompatIcs.getText(record); + } + + public int getToIndex(Object record) { + return AccessibilityRecordCompatIcs.getToIndex(record); + } + + public int getWindowId(Object record) { + return AccessibilityRecordCompatIcs.getWindowId(record); + } + + public boolean isChecked(Object record) { + return AccessibilityRecordCompatIcs.isChecked(record); + } + + public boolean isEnabled(Object record) { + return AccessibilityRecordCompatIcs.isEnabled(record); + } + + public boolean isFullScreen(Object record) { + return AccessibilityRecordCompatIcs.isFullScreen(record); + } + + public boolean isPassword(Object record) { + return AccessibilityRecordCompatIcs.isPassword(record); + } + + public boolean isScrollable(Object record) { + return AccessibilityRecordCompatIcs.isScrollable(record); + } + + public void recycle(Object record) { + AccessibilityRecordCompatIcs.recycle(record); + } + + public void setAddedCount(Object record, int addedCount) { + AccessibilityRecordCompatIcs.setAddedCount(record, addedCount); + } + + public void setBeforeText(Object record, CharSequence beforeText) { + AccessibilityRecordCompatIcs.setBeforeText(record, beforeText); + } + + public void setChecked(Object record, boolean isChecked) { + AccessibilityRecordCompatIcs.setChecked(record, isChecked); + } + + public void setClassName(Object record, CharSequence className) { + AccessibilityRecordCompatIcs.setClassName(record, className); + } + + public void setContentDescription(Object record, CharSequence contentDescription) { + AccessibilityRecordCompatIcs.setContentDescription(record, contentDescription); + } + + public void setCurrentItemIndex(Object record, int currentItemIndex) { + AccessibilityRecordCompatIcs.setCurrentItemIndex(record, currentItemIndex); + } + + public void setEnabled(Object record, boolean isEnabled) { + AccessibilityRecordCompatIcs.setEnabled(record, isEnabled); + } + + public void setFromIndex(Object record, int fromIndex) { + AccessibilityRecordCompatIcs.setFromIndex(record, fromIndex); + } + + public void setFullScreen(Object record, boolean isFullScreen) { + AccessibilityRecordCompatIcs.setFullScreen(record, isFullScreen); + } + + public void setItemCount(Object record, int itemCount) { + AccessibilityRecordCompatIcs.setItemCount(record, itemCount); + } + + public void setMaxScrollX(Object record, int maxScrollX) { +// TODO: Uncomment when these APIs become public +// AccessibilityRecordCompatIcs.setMaxScrollX(record, maxScrollX); + } + + public void setMaxScrollY(Object record, int maxScrollY) { +// TODO: Uncomment when these APIs become public +// AccessibilityRecordCompatIcs.setMaxScrollY(record, maxScrollY); + } + + public void setParcelableData(Object record, Parcelable parcelableData) { + AccessibilityRecordCompatIcs.setParcelableData(record, parcelableData); + } + + public void setPassword(Object record, boolean isPassword) { + AccessibilityRecordCompatIcs.setPassword(record, isPassword); + } + + public void setRemovedCount(Object record, int removedCount) { + AccessibilityRecordCompatIcs.setRemovedCount(record, removedCount); + } + + public void setScrollX(Object record, int scrollX) { + AccessibilityRecordCompatIcs.setScrollX(record, scrollX); + } + + public void setScrollY(Object record, int scrollY) { + AccessibilityRecordCompatIcs.setScrollY(record, scrollY); + } + + public void setScrollable(Object record, boolean scrollable) { + AccessibilityRecordCompatIcs.setScrollable(record, scrollable); + } + + public void setSource(Object record, View source) { + AccessibilityRecordCompatIcs.setSource(record, source); + } + + public void setToIndex(Object record, int toIndex) { + AccessibilityRecordCompatIcs.setToIndex(record, toIndex); + } + } +*/ + static { + //if (Build.VERSION.SDK_INT >= 14) { // ICS + // IMPL = new AccessibilityRecordIcsImpl(); + //} else { + IMPL = new AccessibilityRecordStubImpl(); + //} + } + + private static final AccessibilityRecordImpl IMPL; + + private final Object mRecord; + + /* + * Hide constructor from clients. + */ + public AccessibilityRecordCompat(Object record) { + mRecord = record; + } + + /** + * @return The wrapped implementation. + */ + public Object getImpl() { + return mRecord; + } + + /** + * Returns a cached instance if such is available or a new one is + * instantiated. The instance is initialized with data from the + * given record. + * + * @return An instance. + */ + public static AccessibilityRecordCompat obtain(AccessibilityRecordCompat record) { + return new AccessibilityRecordCompat(IMPL.obtain(record.mRecord)); + } + + /** + * Returns a cached instance if such is available or a new one is + * instantiated. + * + * @return An instance. + */ + public static AccessibilityRecordCompat obtain() { + return new AccessibilityRecordCompat(IMPL.obtain()); + } + + /** + * Sets the event source. + * + * @param source The source. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setSource(View source) { + IMPL.setSource(mRecord, source); + } + + /** + * Gets the {@link android.view.accessibility.AccessibilityNodeInfo} of + * the event source. + *

+ * Note: It is a client responsibility to recycle the + * received info by calling + * {@link android.view.accessibility.AccessibilityNodeInfo#recycle() + * AccessibilityNodeInfo#recycle()} to avoid creating of multiple instances. + *

+ * + * @return The info of the source. + */ + public AccessibilityNodeInfoCompat getSource() { + return new AccessibilityNodeInfoCompat(IMPL.getSource(mRecord)); + } + + /** + * Gets the id of the window from which the event comes from. + * + * @return The window id. + */ + public int getWindowId() { + return IMPL.getWindowId(mRecord); + } + + /** + * Gets if the source is checked. + * + * @return True if the view is checked, false otherwise. + */ + public boolean isChecked() { + return IMPL.isChecked(mRecord); + } + + /** + * Sets if the source is checked. + * + * @param isChecked True if the view is checked, false otherwise. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setChecked(boolean isChecked) { + IMPL.setChecked(mRecord, isChecked); + } + + /** + * Gets if the source is enabled. + * + * @return True if the view is enabled, false otherwise. + */ + public boolean isEnabled() { + return IMPL.isEnabled(mRecord); + } + + /** + * Sets if the source is enabled. + * + * @param isEnabled True if the view is enabled, false otherwise. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setEnabled(boolean isEnabled) { + IMPL.setEnabled(mRecord, isEnabled); + } + + /** + * Gets if the source is a password field. + * + * @return True if the view is a password field, false otherwise. + */ + public boolean isPassword() { + return IMPL.isPassword(mRecord); + } + + /** + * Sets if the source is a password field. + * + * @param isPassword True if the view is a password field, false otherwise. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setPassword(boolean isPassword) { + IMPL.setPassword(mRecord, isPassword); + } + + /** + * Gets if the source is taking the entire screen. + * + * @return True if the source is full screen, false otherwise. + */ + public boolean isFullScreen() { + return IMPL.isFullScreen(mRecord); + } + + /** + * Sets if the source is taking the entire screen. + * + * @param isFullScreen True if the source is full screen, false otherwise. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setFullScreen(boolean isFullScreen) { + IMPL.setFullScreen(mRecord, isFullScreen); + } + + /** + * Gets if the source is scrollable. + * + * @return True if the source is scrollable, false otherwise. + */ + public boolean isScrollable() { + return IMPL.isScrollable(mRecord); + } + + /** + * Sets if the source is scrollable. + * + * @param scrollable True if the source is scrollable, false otherwise. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setScrollable(boolean scrollable) { + IMPL.setScrollable(mRecord, scrollable); + } + + /** + * Gets the number of items that can be visited. + * + * @return The number of items. + */ + public int getItemCount() { + return IMPL.getItemCount(mRecord); + } + + /** + * Sets the number of items that can be visited. + * + * @param itemCount The number of items. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setItemCount(int itemCount) { + IMPL.setItemCount(mRecord, itemCount); + } + + /** + * Gets the index of the source in the list of items the can be visited. + * + * @return The current item index. + */ + public int getCurrentItemIndex() { + return IMPL.getCurrentItemIndex(mRecord); + } + + /** + * Sets the index of the source in the list of items that can be visited. + * + * @param currentItemIndex The current item index. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setCurrentItemIndex(int currentItemIndex) { + IMPL.setCurrentItemIndex(mRecord, currentItemIndex); + } + + /** + * Gets the index of the first character of the changed sequence, + * or the beginning of a text selection or the index of the first + * visible item when scrolling. + * + * @return The index of the first character or selection + * start or the first visible item. + */ + public int getFromIndex() { + return IMPL.getFromIndex(mRecord); + } + + /** + * Sets the index of the first character of the changed sequence + * or the beginning of a text selection or the index of the first + * visible item when scrolling. + * + * @param fromIndex The index of the first character or selection + * start or the first visible item. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setFromIndex(int fromIndex) { + IMPL.setFromIndex(mRecord, fromIndex); + } + + /** + * Gets the index of text selection end or the index of the last + * visible item when scrolling. + * + * @return The index of selection end or last item index. + */ + public int getToIndex() { + return IMPL.getToIndex(mRecord); + } + + /** + * Sets the index of text selection end or the index of the last + * visible item when scrolling. + * + * @param toIndex The index of selection end or last item index. + */ + public void setToIndex(int toIndex) { + IMPL.setToIndex(mRecord, toIndex); + } + + /** + * Gets the scroll offset of the source left edge in pixels. + * + * @return The scroll. + */ + public int getScrollX() { + return IMPL.getScrollX(mRecord); + } + + /** + * Sets the scroll offset of the source left edge in pixels. + * + * @param scrollX The scroll. + */ + public void setScrollX(int scrollX) { + IMPL.setScrollX(mRecord, scrollX); + } + + /** + * Gets the scroll offset of the source top edge in pixels. + * + * @return The scroll. + */ + public int getScrollY() { + return IMPL.getScrollY(mRecord); + } + + /** + * Sets the scroll offset of the source top edge in pixels. + * + * @param scrollY The scroll. + */ + public void setScrollY(int scrollY) { + IMPL.setScrollY(mRecord, scrollY); + } + +// TODO: Uncomment when these APIs become public +// /** +// * Gets the max scroll offset of the source left edge in pixels. +// * +// * @return The max scroll. +// */ +// public int getMaxScrollX() { +// return IMPL.getMaxScrollX(mRecord); +// } +// /** +// * Sets the max scroll offset of the source left edge in pixels. +// * +// * @param maxScrollX The max scroll. +// */ +// public void setMaxScrollX(int maxScrollX) { +// IMPL.setMaxScrollX(mRecord, maxScrollX); +// } +// +// /** +// * Gets the max scroll offset of the source top edge in pixels. +// * +// * @return The max scroll. +// */ +// public int getMaxScrollY() { +// return IMPL.getMaxScrollY(mRecord); +// } +// +// /** +// * Sets the max scroll offset of the source top edge in pixels. +// * +// * @param maxScrollY The max scroll. +// */ +// public void setMaxScrollY(int maxScrollY) { +// IMPL.setMaxScrollY(mRecord, maxScrollY); +// } + + /** + * Gets the number of added characters. + * + * @return The number of added characters. + */ + public int getAddedCount() { + return IMPL.getAddedCount(mRecord); + } + + /** + * Sets the number of added characters. + * + * @param addedCount The number of added characters. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setAddedCount(int addedCount) { + IMPL.setAddedCount(mRecord, addedCount); + } + + /** + * Gets the number of removed characters. + * + * @return The number of removed characters. + */ + public int getRemovedCount() { + return IMPL.getRemovedCount(mRecord); + } + + /** + * Sets the number of removed characters. + * + * @param removedCount The number of removed characters. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setRemovedCount(int removedCount) { + IMPL.setRemovedCount(mRecord, removedCount); + } + + /** + * Gets the class name of the source. + * + * @return The class name. + */ + public CharSequence getClassName() { + return IMPL.getClassName(mRecord); + } + + /** + * Sets the class name of the source. + * + * @param className The lass name. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setClassName(CharSequence className) { + IMPL.setClassName(mRecord, className); + } + + /** + * Gets the text of the event. The index in the list represents the priority + * of the text. Specifically, the lower the index the higher the priority. + * + * @return The text. + */ + public List getText() { + return IMPL.getText(mRecord); + } + + /** + * Sets the text before a change. + * + * @return The text before the change. + */ + public CharSequence getBeforeText() { + return IMPL.getBeforeText(mRecord); + } + + /** + * Sets the text before a change. + * + * @param beforeText The text before the change. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setBeforeText(CharSequence beforeText) { + IMPL.setBeforeText(mRecord, beforeText); + } + + /** + * Gets the description of the source. + * + * @return The description. + */ + public CharSequence getContentDescription() { + return IMPL.getContentDescription(mRecord); + } + + /** + * Sets the description of the source. + * + * @param contentDescription The description. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setContentDescription(CharSequence contentDescription) { + IMPL.setContentDescription(mRecord, contentDescription); + } + + /** + * Gets the {@link Parcelable} data. + * + * @return The parcelable data. + */ + public Parcelable getParcelableData() { + return IMPL.getParcelableData(mRecord); + } + + /** + * Sets the {@link Parcelable} data of the event. + * + * @param parcelableData The parcelable data. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setParcelableData(Parcelable parcelableData) { + IMPL.setParcelableData(mRecord, parcelableData); + } + + /** + * Return an instance back to be reused. + *

+ * Note: You must not touch the object after calling this + * function. + *

+ * + * @throws IllegalStateException If the record is already recycled. + */ + public void recycle() { + IMPL.recycle(mRecord); + } + + @Override + public int hashCode() { + return (mRecord == null) ? 0 : mRecord.hashCode(); + } + + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + AccessibilityRecordCompat other = (AccessibilityRecordCompat) obj; + if (mRecord == null) { + if (other.mRecord != null) { + return false; + } + } else if (!mRecord.equals(other.mRecord)) { + return false; + } + return true; + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java new file mode 100644 index 000000000..ed7190788 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java @@ -0,0 +1,212 @@ +/* + * 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 android.support.v4.view.accessibility; + +//import android.os.Parcelable; +//import android.view.View; +//import android.view.accessibility.AccessibilityRecord; + +//import java.util.List; + +/** + * ICS specific AccessibilityRecord API implementation. + */ +class AccessibilityRecordCompatIcs { +/* + public static Object obtain() { + return AccessibilityRecord.obtain(); + } + + public static Object obtain(Object record) { + return AccessibilityRecord.obtain((AccessibilityRecord) record); + } + + public static int getAddedCount(Object record) { + return ((AccessibilityRecord) record).getAddedCount(); + } + + public static CharSequence getBeforeText(Object record) { + return ((AccessibilityRecord) record).getBeforeText(); + } + + public static CharSequence getClassName(Object record) { + return ((AccessibilityRecord) record).getClassName(); + } + + public static CharSequence getContentDescription(Object record) { + return ((AccessibilityRecord) record).getContentDescription(); + } + + public static int getCurrentItemIndex(Object record) { + return ((AccessibilityRecord) record).getCurrentItemIndex(); + } + + public static int getFromIndex(Object record) { + return ((AccessibilityRecord) record).getFromIndex(); + } + + public static int getItemCount(Object record) { + return ((AccessibilityRecord) record).getItemCount(); + } + +// TODO: Uncomment when these APIs become public +// public static int getMaxScrollX(Object record) { +// return ((AccessibilityRecord) record).getMaxScrollX(); +// } +// +// public static int getMaxScrollY(Object record) { +// return ((AccessibilityRecord) record).getMaxScrollY(); +// } + + public static Parcelable getParcelableData(Object record) { + return ((AccessibilityRecord) record).getParcelableData(); + } + + public static int getRemovedCount(Object record) { + return ((AccessibilityRecord) record).getRemovedCount(); + } + + public static int getScrollX(Object record) { + return ((AccessibilityRecord) record).getScrollX(); + } + + public static int getScrollY(Object record) { + return ((AccessibilityRecord) record).getScrollY(); + } + + public static Object getSource(Object record) { + return ((AccessibilityRecord) record).getSource(); + } + + public static List getText(Object record) { + return ((AccessibilityRecord) record).getText(); + } + + public static int getToIndex(Object record) { + return ((AccessibilityRecord) record).getToIndex(); + } + + public static int getWindowId(Object record) { + return ((AccessibilityRecord) record).getWindowId(); + } + + public static boolean isChecked(Object record) { + return ((AccessibilityRecord) record).isChecked(); + } + + public static boolean isEnabled(Object record) { + return ((AccessibilityRecord) record).isEnabled(); + } + + public static boolean isFullScreen(Object record) { + return ((AccessibilityRecord) record).isFullScreen(); + } + + public static boolean isPassword(Object record) { + return ((AccessibilityRecord) record).isPassword(); + } + + public static boolean isScrollable(Object record) { + return ((AccessibilityRecord) record).isScrollable(); + } + + public static void recycle(Object record) { + ((AccessibilityRecord) record).recycle(); + } + + public static void setAddedCount(Object record, int addedCount) { + ((AccessibilityRecord) record).setAddedCount(addedCount); + } + + public static void setBeforeText(Object record, CharSequence beforeText) { + ((AccessibilityRecord) record).setBeforeText(beforeText); + } + + public static void setChecked(Object record, boolean isChecked) { + ((AccessibilityRecord) record).setChecked(isChecked); + } + + public static void setClassName(Object record, CharSequence className) { + ((AccessibilityRecord) record).setClassName(className); + } + + public static void setContentDescription(Object record, CharSequence contentDescription) { + ((AccessibilityRecord) record).setContentDescription(contentDescription); + } + + public static void setCurrentItemIndex(Object record, int currentItemIndex) { + ((AccessibilityRecord) record).setCurrentItemIndex(currentItemIndex); + } + + public static void setEnabled(Object record, boolean isEnabled) { + ((AccessibilityRecord) record).setEnabled(isEnabled); + } + + public static void setFromIndex(Object record, int fromIndex) { + ((AccessibilityRecord) record).setFromIndex(fromIndex); + } + + public static void setFullScreen(Object record, boolean isFullScreen) { + ((AccessibilityRecord) record).setFullScreen(isFullScreen); + } + + public static void setItemCount(Object record, int itemCount) { + ((AccessibilityRecord) record).setItemCount(itemCount); + } + +// TODO: Uncomment when these APIs become public +// public static void setMaxScrollX(Object record, int maxScrollX) { +// ((AccessibilityRecord) record).setMaxScrollX(maxScrollX); +// } +// +// public static void setMaxScrollY(Object record, int maxScrollY) { +// ((AccessibilityRecord) record).setMaxScrollY(maxScrollY); +// } + + public static void setParcelableData(Object record, Parcelable parcelableData) { + ((AccessibilityRecord) record).setParcelableData(parcelableData); + } + + public static void setPassword(Object record, boolean isPassword) { + ((AccessibilityRecord) record).setPassword(isPassword); + } + + public static void setRemovedCount(Object record, int removedCount) { + ((AccessibilityRecord) record).setRemovedCount(removedCount); + } + + public static void setScrollX(Object record, int scrollX) { + ((AccessibilityRecord) record).setScrollX(scrollX); + } + + public static void setScrollY(Object record, int scrollY) { + ((AccessibilityRecord) record).setScrollY(scrollY); + } + + public static void setScrollable(Object record, boolean scrollable) { + ((AccessibilityRecord) record).setScrollable(scrollable); + } + + public static void setSource(Object record, View source) { + ((AccessibilityRecord) record).setSource(source); + } + + public static void setToIndex(Object record, int toIndex) { + ((AccessibilityRecord) record).setToIndex(toIndex); + } +*/ +} diff --git a/actionbarsherlock/library/src/android/support/v4/widget/CursorAdapter.java b/actionbarsherlock/library/src/android/support/v4/widget/CursorAdapter.java new file mode 100644 index 000000000..cf76fbf49 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/widget/CursorAdapter.java @@ -0,0 +1,484 @@ +/* + * 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 android.support.v4.widget; + +import android.content.Context; +import android.database.ContentObserver; +import android.database.Cursor; +import android.database.DataSetObserver; +import android.os.Handler; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.Filter; +import android.widget.FilterQueryProvider; +import android.widget.Filterable; + +/** + * Static library support version of the framework's {@link android.widget.CursorAdapter}. + * Used to write apps that run on platforms prior to Android 3.0. When running + * on Android 3.0 or above, this implementation is still used; it does not try + * to switch to the framework's implementation. See the framework SDK + * documentation for a class overview. + */ +public abstract class CursorAdapter extends BaseAdapter implements Filterable, + CursorFilter.CursorFilterClient { + /** + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected boolean mDataValid; + /** + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected boolean mAutoRequery; + /** + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected Cursor mCursor; + /** + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected Context mContext; + /** + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected int mRowIDColumn; + /** + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected ChangeObserver mChangeObserver; + /** + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected DataSetObserver mDataSetObserver; + /** + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected CursorFilter mCursorFilter; + /** + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected FilterQueryProvider mFilterQueryProvider; + + /** + * If set the adapter will call requery() on the cursor whenever a content change + * notification is delivered. Implies {@link #FLAG_REGISTER_CONTENT_OBSERVER}. + * + * @deprecated This option is discouraged, as it results in Cursor queries + * being performed on the application's UI thread and thus can cause poor + * responsiveness or even Application Not Responding errors. As an alternative, + * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}. + */ + @Deprecated + public static final int FLAG_AUTO_REQUERY = 0x01; + + /** + * If set the adapter will register a content observer on the cursor and will call + * {@link #onContentChanged()} when a notification comes in. Be careful when + * using this flag: you will need to unset the current Cursor from the adapter + * to avoid leaks due to its registered observers. This flag is not needed + * when using a CursorAdapter with a + * {@link android.content.CursorLoader}. + */ + public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02; + + /** + * Constructor that always enables auto-requery. + * + * @deprecated This option is discouraged, as it results in Cursor queries + * being performed on the application's UI thread and thus can cause poor + * responsiveness or even Application Not Responding errors. As an alternative, + * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}. + * + * @param c The cursor from which to get the data. + * @param context The context + */ + @Deprecated + public CursorAdapter(Context context, Cursor c) { + init(context, c, FLAG_AUTO_REQUERY); + } + + /** + * Constructor that allows control over auto-requery. It is recommended + * you not use this, but instead {@link #CursorAdapter(Context, Cursor, int)}. + * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER} + * will always be set. + * + * @param c The cursor from which to get the data. + * @param context The context + * @param autoRequery If true the adapter will call requery() on the + * cursor whenever it changes so the most recent + * data is always displayed. Using true here is discouraged. + */ + public CursorAdapter(Context context, Cursor c, boolean autoRequery) { + init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER); + } + + /** + * Recommended constructor. + * + * @param c The cursor from which to get the data. + * @param context The context + * @param flags Flags used to determine the behavior of the adapter; may + * be any combination of {@link #FLAG_AUTO_REQUERY} and + * {@link #FLAG_REGISTER_CONTENT_OBSERVER}. + */ + public CursorAdapter(Context context, Cursor c, int flags) { + init(context, c, flags); + } + + /** + * @deprecated Don't use this, use the normal constructor. This will + * be removed in the future. + */ + @Deprecated + protected void init(Context context, Cursor c, boolean autoRequery) { + init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER); + } + + void init(Context context, Cursor c, int flags) { + if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) { + flags |= FLAG_REGISTER_CONTENT_OBSERVER; + mAutoRequery = true; + } else { + mAutoRequery = false; + } + boolean cursorPresent = c != null; + mCursor = c; + mDataValid = cursorPresent; + mContext = context; + mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1; + if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) { + mChangeObserver = new ChangeObserver(); + mDataSetObserver = new MyDataSetObserver(); + } else { + mChangeObserver = null; + mDataSetObserver = null; + } + + if (cursorPresent) { + if (mChangeObserver != null) c.registerContentObserver(mChangeObserver); + if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver); + } + } + + /** + * Returns the cursor. + * @return the cursor. + */ + public Cursor getCursor() { + return mCursor; + } + + /** + * @see android.widget.ListAdapter#getCount() + */ + public int getCount() { + if (mDataValid && mCursor != null) { + return mCursor.getCount(); + } else { + return 0; + } + } + + /** + * @see android.widget.ListAdapter#getItem(int) + */ + public Object getItem(int position) { + if (mDataValid && mCursor != null) { + mCursor.moveToPosition(position); + return mCursor; + } else { + return null; + } + } + + /** + * @see android.widget.ListAdapter#getItemId(int) + */ + public long getItemId(int position) { + if (mDataValid && mCursor != null) { + if (mCursor.moveToPosition(position)) { + return mCursor.getLong(mRowIDColumn); + } else { + return 0; + } + } else { + return 0; + } + } + + @Override + public boolean hasStableIds() { + return true; + } + + /** + * @see android.widget.ListAdapter#getView(int, View, ViewGroup) + */ + public View getView(int position, View convertView, ViewGroup parent) { + if (!mDataValid) { + throw new IllegalStateException("this should only be called when the cursor is valid"); + } + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + View v; + if (convertView == null) { + v = newView(mContext, mCursor, parent); + } else { + v = convertView; + } + bindView(v, mContext, mCursor); + return v; + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + if (mDataValid) { + mCursor.moveToPosition(position); + View v; + if (convertView == null) { + v = newDropDownView(mContext, mCursor, parent); + } else { + v = convertView; + } + bindView(v, mContext, mCursor); + return v; + } else { + return null; + } + } + + /** + * Makes a new view to hold the data pointed to by cursor. + * @param context Interface to application's global information + * @param cursor The cursor from which to get the data. The cursor is already + * moved to the correct position. + * @param parent The parent to which the new view is attached to + * @return the newly created view. + */ + public abstract View newView(Context context, Cursor cursor, ViewGroup parent); + + /** + * Makes a new drop down view to hold the data pointed to by cursor. + * @param context Interface to application's global information + * @param cursor The cursor from which to get the data. The cursor is already + * moved to the correct position. + * @param parent The parent to which the new view is attached to + * @return the newly created view. + */ + public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) { + return newView(context, cursor, parent); + } + + /** + * Bind an existing view to the data pointed to by cursor + * @param view Existing view, returned earlier by newView + * @param context Interface to application's global information + * @param cursor The cursor from which to get the data. The cursor is already + * moved to the correct position. + */ + public abstract void bindView(View view, Context context, Cursor cursor); + + /** + * Change the underlying cursor to a new cursor. If there is an existing cursor it will be + * closed. + * + * @param cursor The new cursor to be used + */ + public void changeCursor(Cursor cursor) { + Cursor old = swapCursor(cursor); + if (old != null) { + old.close(); + } + } + + /** + * Swap in a new Cursor, returning the old Cursor. Unlike + * {@link #changeCursor(Cursor)}, the returned old Cursor is not + * closed. + * + * @param newCursor The new cursor to be used. + * @return Returns the previously set Cursor, or null if there wasa not one. + * If the given new Cursor is the same instance is the previously set + * Cursor, null is also returned. + */ + public Cursor swapCursor(Cursor newCursor) { + if (newCursor == mCursor) { + return null; + } + Cursor oldCursor = mCursor; + if (oldCursor != null) { + if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver); + if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver); + } + mCursor = newCursor; + if (newCursor != null) { + if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver); + if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver); + mRowIDColumn = newCursor.getColumnIndexOrThrow("_id"); + mDataValid = true; + // notify the observers about the new cursor + notifyDataSetChanged(); + } else { + mRowIDColumn = -1; + mDataValid = false; + // notify the observers about the lack of a data set + notifyDataSetInvalidated(); + } + return oldCursor; + } + + /** + *

Converts the cursor into a CharSequence. Subclasses should override this + * method to convert their results. The default implementation returns an + * empty String for null values or the default String representation of + * the value.

+ * + * @param cursor the cursor to convert to a CharSequence + * @return a CharSequence representing the value + */ + public CharSequence convertToString(Cursor cursor) { + return cursor == null ? "" : cursor.toString(); + } + + /** + * Runs a query with the specified constraint. This query is requested + * by the filter attached to this adapter. + * + * The query is provided by a + * {@link android.widget.FilterQueryProvider}. + * If no provider is specified, the current cursor is not filtered and returned. + * + * After this method returns the resulting cursor is passed to {@link #changeCursor(Cursor)} + * and the previous cursor is closed. + * + * This method is always executed on a background thread, not on the + * application's main thread (or UI thread.) + * + * Contract: when constraint is null or empty, the original results, + * prior to any filtering, must be returned. + * + * @param constraint the constraint with which the query must be filtered + * + * @return a Cursor representing the results of the new query + * + * @see #getFilter() + * @see #getFilterQueryProvider() + * @see #setFilterQueryProvider(android.widget.FilterQueryProvider) + */ + public Cursor runQueryOnBackgroundThread(CharSequence constraint) { + if (mFilterQueryProvider != null) { + return mFilterQueryProvider.runQuery(constraint); + } + + return mCursor; + } + + public Filter getFilter() { + if (mCursorFilter == null) { + mCursorFilter = new CursorFilter(this); + } + return mCursorFilter; + } + + /** + * Returns the query filter provider used for filtering. When the + * provider is null, no filtering occurs. + * + * @return the current filter query provider or null if it does not exist + * + * @see #setFilterQueryProvider(android.widget.FilterQueryProvider) + * @see #runQueryOnBackgroundThread(CharSequence) + */ + public FilterQueryProvider getFilterQueryProvider() { + return mFilterQueryProvider; + } + + /** + * Sets the query filter provider used to filter the current Cursor. + * The provider's + * {@link android.widget.FilterQueryProvider#runQuery(CharSequence)} + * method is invoked when filtering is requested by a client of + * this adapter. + * + * @param filterQueryProvider the filter query provider or null to remove it + * + * @see #getFilterQueryProvider() + * @see #runQueryOnBackgroundThread(CharSequence) + */ + public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) { + mFilterQueryProvider = filterQueryProvider; + } + + /** + * Called when the {@link ContentObserver} on the cursor receives a change notification. + * The default implementation provides the auto-requery logic, but may be overridden by + * sub classes. + * + * @see ContentObserver#onChange(boolean) + */ + @SuppressWarnings("unused") + protected void onContentChanged() { + if (mAutoRequery && mCursor != null && !mCursor.isClosed()) { + if (false) Log.v("Cursor", "Auto requerying " + mCursor + " due to update"); + mDataValid = mCursor.requery(); + } + } + + private class ChangeObserver extends ContentObserver { + public ChangeObserver() { + super(new Handler()); + } + + @Override + public boolean deliverSelfNotifications() { + return true; + } + + @Override + public void onChange(boolean selfChange) { + onContentChanged(); + } + } + + private class MyDataSetObserver extends DataSetObserver { + @Override + public void onChanged() { + mDataValid = true; + notifyDataSetChanged(); + } + + @Override + public void onInvalidated() { + mDataValid = false; + notifyDataSetInvalidated(); + } + } + +} diff --git a/actionbarsherlock/library/src/android/support/v4/widget/CursorFilter.java b/actionbarsherlock/library/src/android/support/v4/widget/CursorFilter.java new file mode 100644 index 000000000..69b87be27 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/widget/CursorFilter.java @@ -0,0 +1,71 @@ +/* + * 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 android.support.v4.widget; + +import android.database.Cursor; +import android.widget.Filter; + +/** + *

The CursorFilter delegates most of the work to the CursorAdapter. + * Subclasses should override these delegate methods to run the queries + * and convert the results into String that can be used by auto-completion + * widgets.

+ */ +class CursorFilter extends Filter { + + CursorFilterClient mClient; + + interface CursorFilterClient { + CharSequence convertToString(Cursor cursor); + Cursor runQueryOnBackgroundThread(CharSequence constraint); + Cursor getCursor(); + void changeCursor(Cursor cursor); + } + + CursorFilter(CursorFilterClient client) { + mClient = client; + } + + @Override + public CharSequence convertResultToString(Object resultValue) { + return mClient.convertToString((Cursor) resultValue); + } + + @Override + protected FilterResults performFiltering(CharSequence constraint) { + Cursor cursor = mClient.runQueryOnBackgroundThread(constraint); + + FilterResults results = new FilterResults(); + if (cursor != null) { + results.count = cursor.getCount(); + results.values = cursor; + } else { + results.count = 0; + results.values = null; + } + return results; + } + + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + Cursor oldCursor = mClient.getCursor(); + + if (results.values != null && results.values != oldCursor) { + mClient.changeCursor((Cursor) results.values); + } + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/widget/EdgeEffectCompat.java b/actionbarsherlock/library/src/android/support/v4/widget/EdgeEffectCompat.java new file mode 100644 index 000000000..42eb1c1c7 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/widget/EdgeEffectCompat.java @@ -0,0 +1,218 @@ +/* + * 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 android.support.v4.widget; + +import android.content.Context; +import android.graphics.Canvas; + +/** + * Helper for accessing EdgeEffects from newer platform versions. + * + * This class is used to access {@link android.widget.EdgeEffect} on platform versions + * that support it. When running on older platforms it will result in no-ops. It should + * be used by views that wish to use the standard Android visual effects at the edges + * of scrolling containers. + */ +public class EdgeEffectCompat { + private Object mEdgeEffect; + + private static final EdgeEffectImpl IMPL; + + static { + //if (Build.VERSION.SDK_INT >= 14) { // ICS + // IMPL = new EdgeEffectIcsImpl(); + //} else { + IMPL = new BaseEdgeEffectImpl(); + //} + } + + interface EdgeEffectImpl { + public Object newEdgeEffect(Context context); + public void setSize(Object edgeEffect, int width, int height); + public boolean isFinished(Object edgeEffect); + public void finish(Object edgeEffect); + public boolean onPull(Object edgeEffect, float deltaDistance); + public boolean onRelease(Object edgeEffect); + public boolean onAbsorb(Object edgeEffect, int velocity); + public boolean draw(Object edgeEffect, Canvas canvas); + } + + /** + * Null implementation to use pre-ICS + */ + static class BaseEdgeEffectImpl implements EdgeEffectImpl { + public Object newEdgeEffect(Context context) { + return null; + } + + public void setSize(Object edgeEffect, int width, int height) { + } + + public boolean isFinished(Object edgeEffect) { + return true; + } + + public void finish(Object edgeEffect) { + } + + public boolean onPull(Object edgeEffect, float deltaDistance) { + return false; + } + + public boolean onRelease(Object edgeEffect) { + return false; + } + + public boolean onAbsorb(Object edgeEffect, int velocity) { + return false; + } + + public boolean draw(Object edgeEffect, Canvas canvas) { + return false; + } + } +/* + static class EdgeEffectIcsImpl implements EdgeEffectImpl { + public Object newEdgeEffect(Context context) { + return EdgeEffectCompatIcs.newEdgeEffect(context); + } + + public void setSize(Object edgeEffect, int width, int height) { + EdgeEffectCompatIcs.setSize(edgeEffect, width, height); + } + + public boolean isFinished(Object edgeEffect) { + return EdgeEffectCompatIcs.isFinished(edgeEffect); + } + + public void finish(Object edgeEffect) { + EdgeEffectCompatIcs.finish(edgeEffect); + } + + public boolean onPull(Object edgeEffect, float deltaDistance) { + return EdgeEffectCompatIcs.onPull(edgeEffect, deltaDistance); + } + + public boolean onRelease(Object edgeEffect) { + return EdgeEffectCompatIcs.onRelease(edgeEffect); + } + + public boolean onAbsorb(Object edgeEffect, int velocity) { + return EdgeEffectCompatIcs.onAbsorb(edgeEffect, velocity); + } + + public boolean draw(Object edgeEffect, Canvas canvas) { + return EdgeEffectCompatIcs.draw(edgeEffect, canvas); + } + } +*/ + /** + * Construct a new EdgeEffect themed using the given context. + * + *

Note: On platform versions that do not support EdgeEffect, all operations + * on the newly constructed object will be mocked/no-ops.

+ * + * @param context Context to use for theming the effect + */ + public EdgeEffectCompat(Context context) { + mEdgeEffect = IMPL.newEdgeEffect(context); + } + + /** + * Set the size of this edge effect in pixels. + * + * @param width Effect width in pixels + * @param height Effect height in pixels + */ + public void setSize(int width, int height) { + IMPL.setSize(mEdgeEffect, width, height); + } + + /** + * Reports if this EdgeEffectCompat's animation is finished. If this method returns false + * after a call to {@link #draw(Canvas)} the host widget should schedule another + * drawing pass to continue the animation. + * + * @return true if animation is finished, false if drawing should continue on the next frame. + */ + public boolean isFinished() { + return IMPL.isFinished(mEdgeEffect); + } + + /** + * Immediately finish the current animation. + * After this call {@link #isFinished()} will return true. + */ + public void finish() { + IMPL.finish(mEdgeEffect); + } + + /** + * A view should call this when content is pulled away from an edge by the user. + * This will update the state of the current visual effect and its associated animation. + * The host view should always {@link android.view.View#invalidate()} if this method + * returns true and draw the results accordingly. + * + * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to + * 1.f (full length of the view) or negative values to express change + * back toward the edge reached to initiate the effect. + * @return true if the host view should call invalidate, false if it should not. + */ + public boolean onPull(float deltaDistance) { + return IMPL.onPull(mEdgeEffect, deltaDistance); + } + + /** + * Call when the object is released after being pulled. + * This will begin the "decay" phase of the effect. After calling this method + * the host view should {@link android.view.View#invalidate()} if this method + * returns true and thereby draw the results accordingly. + * + * @return true if the host view should invalidate, false if it should not. + */ + public boolean onRelease() { + return IMPL.onRelease(mEdgeEffect); + } + + /** + * Call when the effect absorbs an impact at the given velocity. + * Used when a fling reaches the scroll boundary. + * + *

When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller}, + * the method getCurrVelocity will provide a reasonable approximation + * to use here.

+ * + * @param velocity Velocity at impact in pixels per second. + * @return true if the host view should invalidate, false if it should not. + */ + public boolean onAbsorb(int velocity) { + return IMPL.onAbsorb(mEdgeEffect, velocity); + } + + /** + * Draw into the provided canvas. Assumes that the canvas has been rotated + * accordingly and the size has been set. The effect will be drawn the full + * width of X=0 to X=width, beginning from Y=0 and extending to some factor < + * 1.f of height. + * + * @param canvas Canvas to draw into + * @return true if drawing should continue beyond this frame to continue the + * animation + */ + public boolean draw(Canvas canvas) { + return IMPL.draw(mEdgeEffect, canvas); + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/widget/EdgeEffectCompatIcs.java b/actionbarsherlock/library/src/android/support/v4/widget/EdgeEffectCompatIcs.java new file mode 100644 index 000000000..4e0e56131 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/widget/EdgeEffectCompatIcs.java @@ -0,0 +1,66 @@ +/* + * 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 android.support.v4.widget; + +//import android.content.Context; +//import android.graphics.Canvas; +//import android.widget.EdgeEffect; + +/** + * Stub implementation that contains a real EdgeEffect on ICS. + * + * This class is an implementation detail for EdgeEffectCompat + * and should not be used directly. + */ +class EdgeEffectCompatIcs { +/* + public static Object newEdgeEffect(Context context) { + return new EdgeEffect(context); + } + + public static void setSize(Object edgeEffect, int width, int height) { + ((EdgeEffect) edgeEffect).setSize(width, height); + } + + public static boolean isFinished(Object edgeEffect) { + return ((EdgeEffect) edgeEffect).isFinished(); + } + + public static void finish(Object edgeEffect) { + ((EdgeEffect) edgeEffect).finish(); + } + + public static boolean onPull(Object edgeEffect, float deltaDistance) { + ((EdgeEffect) edgeEffect).onPull(deltaDistance); + return true; + } + + public static boolean onRelease(Object edgeEffect) { + EdgeEffect eff = (EdgeEffect) edgeEffect; + eff.onRelease(); + return eff.isFinished(); + } + + public static boolean onAbsorb(Object edgeEffect, int velocity) { + ((EdgeEffect) edgeEffect).onAbsorb(velocity); + return true; + } + + public static boolean draw(Object edgeEffect, Canvas canvas) { + return ((EdgeEffect) edgeEffect).draw(canvas); + } +*/ +} diff --git a/actionbarsherlock/library/src/android/support/v4/widget/ResourceCursorAdapter.java b/actionbarsherlock/library/src/android/support/v4/widget/ResourceCursorAdapter.java new file mode 100644 index 000000000..12dce7871 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/widget/ResourceCursorAdapter.java @@ -0,0 +1,131 @@ +/* + * 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 android.support.v4.widget; + +import android.content.Context; +import android.database.Cursor; +import android.view.View; +import android.view.ViewGroup; +import android.view.LayoutInflater; + +/** + * Static library support version of the framework's {@link android.widget.ResourceCursorAdapter}. + * Used to write apps that run on platforms prior to Android 3.0. When running + * on Android 3.0 or above, this implementation is still used; it does not try + * to switch to the framework's implementation. See the framework SDK + * documentation for a class overview. + */ +public abstract class ResourceCursorAdapter extends CursorAdapter { + private int mLayout; + + private int mDropDownLayout; + + private LayoutInflater mInflater; + + /** + * Constructor the enables auto-requery. + * + * @deprecated This option is discouraged, as it results in Cursor queries + * being performed on the application's UI thread and thus can cause poor + * responsiveness or even Application Not Responding errors. As an alternative, + * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}. + * + * @param context The context where the ListView associated with this adapter is running + * @param layout resource identifier of a layout file that defines the views + * for this list item. Unless you override them later, this will + * define both the item views and the drop down views. + */ + @Deprecated + public ResourceCursorAdapter(Context context, int layout, Cursor c) { + super(context, c); + mLayout = mDropDownLayout = layout; + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + /** + * Constructor with default behavior as per + * {@link CursorAdapter#CursorAdapter(Context, Cursor, boolean)}; it is recommended + * you not use this, but instead {@link #ResourceCursorAdapter(Context, int, Cursor, int)}. + * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER} + * will always be set. + * + * @param context The context where the ListView associated with this adapter is running + * @param layout resource identifier of a layout file that defines the views + * for this list item. Unless you override them later, this will + * define both the item views and the drop down views. + * @param c The cursor from which to get the data. + * @param autoRequery If true the adapter will call requery() on the + * cursor whenever it changes so the most recent + * data is always displayed. Using true here is discouraged. + */ + public ResourceCursorAdapter(Context context, int layout, Cursor c, boolean autoRequery) { + super(context, c, autoRequery); + mLayout = mDropDownLayout = layout; + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + /** + * Standard constructor. + * + * @param context The context where the ListView associated with this adapter is running + * @param layout Resource identifier of a layout file that defines the views + * for this list item. Unless you override them later, this will + * define both the item views and the drop down views. + * @param c The cursor from which to get the data. + * @param flags Flags used to determine the behavior of the adapter, + * as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}. + */ + public ResourceCursorAdapter(Context context, int layout, Cursor c, int flags) { + super(context, c, flags); + mLayout = mDropDownLayout = layout; + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + /** + * Inflates view(s) from the specified XML file. + * + * @see android.widget.CursorAdapter#newView(android.content.Context, + * android.database.Cursor, ViewGroup) + */ + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(mLayout, parent, false); + } + + @Override + public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(mDropDownLayout, parent, false); + } + + /** + *

Sets the layout resource of the item views.

+ * + * @param layout the layout resources used to create item views + */ + public void setViewResource(int layout) { + mLayout = layout; + } + + /** + *

Sets the layout resource of the drop down views.

+ * + * @param dropDownLayout the layout resources used to create drop down views + */ + public void setDropDownViewResource(int dropDownLayout) { + mDropDownLayout = dropDownLayout; + } +} diff --git a/actionbarsherlock/library/src/android/support/v4/widget/SimpleCursorAdapter.java b/actionbarsherlock/library/src/android/support/v4/widget/SimpleCursorAdapter.java new file mode 100644 index 000000000..4b81b11f3 --- /dev/null +++ b/actionbarsherlock/library/src/android/support/v4/widget/SimpleCursorAdapter.java @@ -0,0 +1,398 @@ +/* + * 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 android.support.v4.widget; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +/** + * Static library support version of the framework's {@link android.widget.SimpleCursorAdapter}. + * Used to write apps that run on platforms prior to Android 3.0. When running + * on Android 3.0 or above, this implementation is still used; it does not try + * to switch to the framework's implementation. See the framework SDK + * documentation for a class overview. + */ +public class SimpleCursorAdapter extends ResourceCursorAdapter { + /** + * A list of columns containing the data to bind to the UI. + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected int[] mFrom; + /** + * A list of View ids representing the views to which the data must be bound. + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected int[] mTo; + + private int mStringConversionColumn = -1; + private CursorToStringConverter mCursorToStringConverter; + private ViewBinder mViewBinder; + + String[] mOriginalFrom; + + /** + * Constructor the enables auto-requery. + * + * @deprecated This option is discouraged, as it results in Cursor queries + * being performed on the application's UI thread and thus can cause poor + * responsiveness or even Application Not Responding errors. As an alternative, + * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}. + */ + @Deprecated + public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) { + super(context, layout, c); + mTo = to; + mOriginalFrom = from; + findColumns(from); + } + + /** + * Standard constructor. + * + * @param context The context where the ListView associated with this + * SimpleListItemFactory is running + * @param layout resource identifier of a layout file that defines the views + * for this list item. The layout file should include at least + * those named views defined in "to" + * @param c The database cursor. Can be null if the cursor is not available yet. + * @param from A list of column names representing the data to bind to the UI. Can be null + * if the cursor is not available yet. + * @param to The views that should display column in the "from" parameter. + * These should all be TextViews. The first N views in this list + * are given the values of the first N columns in the from + * parameter. Can be null if the cursor is not available yet. + * @param flags Flags used to determine the behavior of the adapter, + * as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}. + */ + public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, + int[] to, int flags) { + super(context, layout, c, flags); + mTo = to; + mOriginalFrom = from; + findColumns(from); + } + + /** + * Binds all of the field names passed into the "to" parameter of the + * constructor with their corresponding cursor columns as specified in the + * "from" parameter. + * + * Binding occurs in two phases. First, if a + * {@link android.widget.SimpleCursorAdapter.ViewBinder} is available, + * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)} + * is invoked. If the returned value is true, binding has occured. If the + * returned value is false and the view to bind is a TextView, + * {@link #setViewText(TextView, String)} is invoked. If the returned value is + * false and the view to bind is an ImageView, + * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate + * binding can be found, an {@link IllegalStateException} is thrown. + * + * @throws IllegalStateException if binding cannot occur + * + * @see android.widget.CursorAdapter#bindView(android.view.View, + * android.content.Context, android.database.Cursor) + * @see #getViewBinder() + * @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder) + * @see #setViewImage(ImageView, String) + * @see #setViewText(TextView, String) + */ + @Override + public void bindView(View view, Context context, Cursor cursor) { + final ViewBinder binder = mViewBinder; + final int count = mTo.length; + final int[] from = mFrom; + final int[] to = mTo; + + for (int i = 0; i < count; i++) { + final View v = view.findViewById(to[i]); + if (v != null) { + boolean bound = false; + if (binder != null) { + bound = binder.setViewValue(v, cursor, from[i]); + } + + if (!bound) { + String text = cursor.getString(from[i]); + if (text == null) { + text = ""; + } + + if (v instanceof TextView) { + setViewText((TextView) v, text); + } else if (v instanceof ImageView) { + setViewImage((ImageView) v, text); + } else { + throw new IllegalStateException(v.getClass().getName() + " is not a " + + " view that can be bounds by this SimpleCursorAdapter"); + } + } + } + } + } + + /** + * Returns the {@link ViewBinder} used to bind data to views. + * + * @return a ViewBinder or null if the binder does not exist + * + * @see #bindView(android.view.View, android.content.Context, android.database.Cursor) + * @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder) + */ + public ViewBinder getViewBinder() { + return mViewBinder; + } + + /** + * Sets the binder used to bind data to views. + * + * @param viewBinder the binder used to bind data to views, can be null to + * remove the existing binder + * + * @see #bindView(android.view.View, android.content.Context, android.database.Cursor) + * @see #getViewBinder() + */ + public void setViewBinder(ViewBinder viewBinder) { + mViewBinder = viewBinder; + } + + /** + * Called by bindView() to set the image for an ImageView but only if + * there is no existing ViewBinder or if the existing ViewBinder cannot + * handle binding to an ImageView. + * + * By default, the value will be treated as an image resource. If the + * value cannot be used as an image resource, the value is used as an + * image Uri. + * + * Intended to be overridden by Adapters that need to filter strings + * retrieved from the database. + * + * @param v ImageView to receive an image + * @param value the value retrieved from the cursor + */ + public void setViewImage(ImageView v, String value) { + try { + v.setImageResource(Integer.parseInt(value)); + } catch (NumberFormatException nfe) { + v.setImageURI(Uri.parse(value)); + } + } + + /** + * Called by bindView() to set the text for a TextView but only if + * there is no existing ViewBinder or if the existing ViewBinder cannot + * handle binding to an TextView. + * + * Intended to be overridden by Adapters that need to filter strings + * retrieved from the database. + * + * @param v TextView to receive text + * @param text the text to be set for the TextView + */ + public void setViewText(TextView v, String text) { + v.setText(text); + } + + /** + * Return the index of the column used to get a String representation + * of the Cursor. + * + * @return a valid index in the current Cursor or -1 + * + * @see android.widget.CursorAdapter#convertToString(android.database.Cursor) + * @see #setStringConversionColumn(int) + * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter) + * @see #getCursorToStringConverter() + */ + public int getStringConversionColumn() { + return mStringConversionColumn; + } + + /** + * Defines the index of the column in the Cursor used to get a String + * representation of that Cursor. The column is used to convert the + * Cursor to a String only when the current CursorToStringConverter + * is null. + * + * @param stringConversionColumn a valid index in the current Cursor or -1 to use the default + * conversion mechanism + * + * @see android.widget.CursorAdapter#convertToString(android.database.Cursor) + * @see #getStringConversionColumn() + * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter) + * @see #getCursorToStringConverter() + */ + public void setStringConversionColumn(int stringConversionColumn) { + mStringConversionColumn = stringConversionColumn; + } + + /** + * Returns the converter used to convert the filtering Cursor + * into a String. + * + * @return null if the converter does not exist or an instance of + * {@link android.widget.SimpleCursorAdapter.CursorToStringConverter} + * + * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter) + * @see #getStringConversionColumn() + * @see #setStringConversionColumn(int) + * @see android.widget.CursorAdapter#convertToString(android.database.Cursor) + */ + public CursorToStringConverter getCursorToStringConverter() { + return mCursorToStringConverter; + } + + /** + * Sets the converter used to convert the filtering Cursor + * into a String. + * + * @param cursorToStringConverter the Cursor to String converter, or + * null to remove the converter + * + * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter) + * @see #getStringConversionColumn() + * @see #setStringConversionColumn(int) + * @see android.widget.CursorAdapter#convertToString(android.database.Cursor) + */ + public void setCursorToStringConverter(CursorToStringConverter cursorToStringConverter) { + mCursorToStringConverter = cursorToStringConverter; + } + + /** + * Returns a CharSequence representation of the specified Cursor as defined + * by the current CursorToStringConverter. If no CursorToStringConverter + * has been set, the String conversion column is used instead. If the + * conversion column is -1, the returned String is empty if the cursor + * is null or Cursor.toString(). + * + * @param cursor the Cursor to convert to a CharSequence + * + * @return a non-null CharSequence representing the cursor + */ + @Override + public CharSequence convertToString(Cursor cursor) { + if (mCursorToStringConverter != null) { + return mCursorToStringConverter.convertToString(cursor); + } else if (mStringConversionColumn > -1) { + return cursor.getString(mStringConversionColumn); + } + + return super.convertToString(cursor); + } + + /** + * Create a map from an array of strings to an array of column-id integers in mCursor. + * If mCursor is null, the array will be discarded. + * + * @param from the Strings naming the columns of interest + */ + private void findColumns(String[] from) { + if (mCursor != null) { + int i; + int count = from.length; + if (mFrom == null || mFrom.length != count) { + mFrom = new int[count]; + } + for (i = 0; i < count; i++) { + mFrom[i] = mCursor.getColumnIndexOrThrow(from[i]); + } + } else { + mFrom = null; + } + } + + @Override + public Cursor swapCursor(Cursor c) { + Cursor res = super.swapCursor(c); + // rescan columns in case cursor layout is different + findColumns(mOriginalFrom); + return res; + } + + /** + * Change the cursor and change the column-to-view mappings at the same time. + * + * @param c The database cursor. Can be null if the cursor is not available yet. + * @param from A list of column names representing the data to bind to the UI. Can be null + * if the cursor is not available yet. + * @param to The views that should display column in the "from" parameter. + * These should all be TextViews. The first N views in this list + * are given the values of the first N columns in the from + * parameter. Can be null if the cursor is not available yet. + */ + public void changeCursorAndColumns(Cursor c, String[] from, int[] to) { + mOriginalFrom = from; + mTo = to; + super.changeCursor(c); + findColumns(mOriginalFrom); + } + + /** + * This class can be used by external clients of SimpleCursorAdapter + * to bind values fom the Cursor to views. + * + * You should use this class to bind values from the Cursor to views + * that are not directly supported by SimpleCursorAdapter or to + * change the way binding occurs for views supported by + * SimpleCursorAdapter. + * + * @see SimpleCursorAdapter#bindView(android.view.View, android.content.Context, android.database.Cursor) + * @see SimpleCursorAdapter#setViewImage(ImageView, String) + * @see SimpleCursorAdapter#setViewText(TextView, String) + */ + public static interface ViewBinder { + /** + * Binds the Cursor column defined by the specified index to the specified view. + * + * When binding is handled by this ViewBinder, this method must return true. + * If this method returns false, SimpleCursorAdapter will attempts to handle + * the binding on its own. + * + * @param view the view to bind the data to + * @param cursor the cursor to get the data from + * @param columnIndex the column at which the data can be found in the cursor + * + * @return true if the data was bound to the view, false otherwise + */ + boolean setViewValue(View view, Cursor cursor, int columnIndex); + } + + /** + * This class can be used by external clients of SimpleCursorAdapter + * to define how the Cursor should be converted to a String. + * + * @see android.widget.CursorAdapter#convertToString(android.database.Cursor) + */ + public static interface CursorToStringConverter { + /** + * Returns a CharSequence representing the specified Cursor. + * + * @param cursor the cursor for which a CharSequence representation + * is requested + * + * @return a non-null CharSequence representing the cursor + */ + CharSequence convertToString(Cursor cursor); + } + +} diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/app/ActionBarImpl.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/app/ActionBarImpl.java new file mode 100644 index 000000000..70cf23bc5 --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/app/ActionBarImpl.java @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2011 Jake Wharton + * Copyright (C) 2010 Johan Nilsson + * + * 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.util.ArrayList; +import java.util.List; + +import android.app.Activity; +import android.graphics.drawable.Drawable; +import android.support.v4.app.ActionBar; +import android.support.v4.view.ActionMode; +import android.support.v4.view.MenuItem; +import android.view.LayoutInflater; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.SpinnerAdapter; + +import com.actionbarsherlock.R; +import com.actionbarsherlock.internal.view.menu.ActionMenuItemView; +import com.actionbarsherlock.internal.view.menu.MenuBuilder; +import com.actionbarsherlock.internal.view.menu.MenuItemImpl; +import com.actionbarsherlock.internal.widget.ActionBarContainer; +import com.actionbarsherlock.internal.widget.ActionBarView; + +public final class ActionBarImpl extends ActionBar { + private final Activity mActivity; + + /** Action bar container. */ + private ActionBarContainer mContainerView; + + /** Action bar view. */ + private ActionBarView mActionView; + + /** List of listeners to the menu visibility. */ + private final List mMenuListeners = new ArrayList(); + + private Animation mFadeInAnimation; + private Animation mFadeOutAnimation; + + + + public ActionBarImpl(Activity activity) { + mActivity = activity; + } + + + // ------------------------------------------------------------------------ + // ACTION BAR SHERLOCK SUPPORT + // ------------------------------------------------------------------------ + + @Override + protected ActionBar getPublicInstance() { + return this; + } + + public void init() { + mActionView = (ActionBarView)mActivity.findViewById(R.id.abs__action_bar); + mContainerView = (ActionBarContainer)mActivity.findViewById(R.id.abs__action_bar_container); + + if (mActionView == null) { + throw new IllegalStateException(getClass().getSimpleName() + " can only be used with a screen_*.xml layout"); + } + + mFadeInAnimation = AnimationUtils.loadAnimation(mActivity, android.R.anim.fade_in); + mFadeOutAnimation = AnimationUtils.loadAnimation(mActivity, android.R.anim.fade_out); + + if (mActionView.getTitle() == null) { + mActionView.setTitle(mActivity.getTitle()); + } + } + + public void onMenuInflated(MenuBuilder menu) { + if (mActionView == null) { + return; + } + + final int maxItems = mActivity.getResources().getInteger(R.integer.abs__max_action_buttons); + + //Iterate and grab as many actions as we can up to maxItems honoring + //their showAsAction values + int ifItems = 0; + final int count = menu.size(); + boolean showsActionItemText = menu.getShowsActionItemText(); + List keep = new ArrayList(); + for (int i = 0; i < count; i++) { + MenuItemImpl item = (MenuItemImpl)menu.getItem(i); + + //Items without an icon or custom view are forced into the overflow menu + if (!showsActionItemText && (item.getIcon() == null) && (item.getActionView() == null)) { + continue; + } + if (showsActionItemText && ((item.getTitle() == null) || "".equals(item.getTitle()))) { + continue; + } + + if ((item.getShowAsAction() & MenuItem.SHOW_AS_ACTION_ALWAYS) != 0) { + //Show always therefore add to keep list + keep.add(item); + + if ((keep.size() > maxItems) && (ifItems > 0)) { + //If we have exceeded the max and there are "ifRoom" items + //then iterate backwards to remove one and add it to the + //head of the classic items list. + for (int j = keep.size() - 1; j >= 0; j--) { + if ((keep.get(j).getShowAsAction() & MenuItem.SHOW_AS_ACTION_IF_ROOM) != 0) { + keep.remove(j); + ifItems -= 1; + break; + } + } + } + } else if (((item.getShowAsAction() & MenuItem.SHOW_AS_ACTION_IF_ROOM) != 0) + && (keep.size() < maxItems)) { + //"ifRoom" items are added if we have not exceeded the max. + keep.add(item); + ifItems += 1; + } + } + + //Mark items that will be shown on the action bar as such so they do + //not show up on the activity options menu + mActionView.removeAllItems(); + for (MenuItemImpl item : keep) { + item.setIsShownOnActionBar(true); + + //Get a new item for this menu item + ActionMenuItemView actionItem = mActionView.newItem(); + actionItem.initialize(item, MenuBuilder.TYPE_ACTION_BAR); + + //Associate the itemview with the item so changes will be reflected + item.setItemView(MenuBuilder.TYPE_ACTION_BAR, actionItem); + + //Add to the action bar for display + mActionView.addItem(actionItem); + } + } + + public void onMenuVisibilityChanged(boolean isVisible) { + //Marshal to all listeners + for (OnMenuVisibilityListener listener : mMenuListeners) { + listener.onMenuVisibilityChanged(isVisible); + } + } + + public void setProgressBarIndeterminateVisibility(boolean visible) { + if (mActionView != null) { + mActionView.setProgressBarIndeterminateVisibility(visible); + } + } + + // ------------------------------------------------------------------------ + // ACTION MODE METHODS + // ------------------------------------------------------------------------ + + @Override + protected ActionMode startActionMode(ActionMode.Callback callback) { + throw new RuntimeException("Not implemented."); + } + + // ------------------------------------------------------------------------ + // ACTION BAR METHODS + // ------------------------------------------------------------------------ + + @Override + public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) { + if (!mMenuListeners.contains(listener)) { + mMenuListeners.add(listener); + } + } + + @Override + public void addTab(Tab tab) { + mActionView.addTab(tab); + } + + @Override + public void addTab(Tab tab, boolean setSelected) { + mActionView.addTab(tab, setSelected); + } + + @Override + public void addTab(Tab tab, int position) { + mActionView.addTab(tab, position); + } + + @Override + public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { + mActionView.addTab(tab, position, setSelected); + } + + @Override + public View getCustomView() { + return mActionView.getCustomView(); + } + + @Override + public int getDisplayOptions() { + return mActionView.getDisplayOptions(); + } + + @Override + public int getHeight() { + return mActionView.getHeight(); + } + + @Override + public int getNavigationItemCount() { + switch (mActionView.getNavigationMode()) { + default: + case ActionBar.NAVIGATION_MODE_STANDARD: + return 0; + + case ActionBar.NAVIGATION_MODE_LIST: + SpinnerAdapter dropdownAdapter = mActionView.getDropdownAdapter(); + return (dropdownAdapter != null) ? dropdownAdapter.getCount() : 0; + + case ActionBar.NAVIGATION_MODE_TABS: + if (mActionView.getSelectedTab() == null) { + return -1; + } + return mActionView.getTabCount(); + } + } + + @Override + public int getNavigationMode() { + return mActionView.getNavigationMode(); + } + + @Override + public int getSelectedNavigationIndex() { + switch (mActionView.getNavigationMode()) { + default: + case ActionBar.NAVIGATION_MODE_STANDARD: + return -1; + + case ActionBar.NAVIGATION_MODE_LIST: + return mActionView.getDropdownSelectedPosition(); + + case ActionBar.NAVIGATION_MODE_TABS: + return mActionView.getSelectedTab().getPosition(); + } + } + + @Override + public ActionBar.Tab getSelectedTab() { + return mActionView.getSelectedTab(); + } + + @Override + public CharSequence getSubtitle() { + return mActionView.getSubtitle(); + } + + @Override + public ActionBar.Tab getTabAt(int index) { + return mActionView.getTabAt(index); + } + + @Override + public int getTabCount() { + return mActionView.getTabCount(); + } + + @Override + public CharSequence getTitle() { + return mActionView.getTitle(); + } + + @Override + public void hide() { + if (mContainerView.getVisibility() != View.GONE) { + mContainerView.startAnimation(mFadeOutAnimation); + mContainerView.setVisibility(View.GONE); + } + } + + @Override + public boolean isShowing() { + return mContainerView.getVisibility() == View.VISIBLE; + } + + @Override + public ActionBar.Tab newTab() { + return mActionView.newTab(); + } + + @Override + public void removeAllTabs() { + mActionView.removeAllTabs(); + } + + @Override + public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) { + mMenuListeners.remove(listener); + } + + @Override + public void removeTab(ActionBar.Tab tab) { + removeTabAt(tab.getPosition()); + } + + @Override + public void removeTabAt(int position) { + mActionView.removeTabAt(position); + } + + @Override + public void setBackgroundDrawable(Drawable d) { + mContainerView.setBackgroundDrawable(d); + } + + @Override + public void setCustomView(int resId) { + View view = LayoutInflater.from(mActivity).inflate(resId, mActionView, false); + setCustomView(view); + } + + @Override + public void setCustomView(View view) { + mActionView.setCustomNavigationView(view); + } + + @Override + public void setCustomView(View view, ActionBar.LayoutParams layoutParams) { + view.setLayoutParams(layoutParams); + mActionView.setCustomNavigationView(view); + } + + @Override + public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) { + setDisplayOptions(showHomeAsUp ? ActionBar.DISPLAY_HOME_AS_UP : 0, ActionBar.DISPLAY_HOME_AS_UP); + } + + @Override + public void setDisplayOptions(int options) { + mActionView.setDisplayOptions(options); + } + + @Override + public void setDisplayOptions(int newOptions, int mask) { + mActionView.setDisplayOptions((mActionView.getDisplayOptions() & ~mask) | newOptions); + } + + @Override + public void setDisplayShowCustomEnabled(boolean showCustom) { + setDisplayOptions(showCustom ? ActionBar.DISPLAY_SHOW_CUSTOM : 0, ActionBar.DISPLAY_SHOW_CUSTOM); + } + + @Override + public void setDisplayShowHomeEnabled(boolean showHome) { + setDisplayOptions(showHome ? ActionBar.DISPLAY_SHOW_HOME : 0, ActionBar.DISPLAY_SHOW_HOME); + } + + @Override + public void setDisplayShowTitleEnabled(boolean showTitle) { + setDisplayOptions(showTitle ? ActionBar.DISPLAY_SHOW_TITLE : 0, ActionBar.DISPLAY_SHOW_TITLE); + } + + @Override + public void setDisplayUseLogoEnabled(boolean useLogo) { + setDisplayOptions(useLogo ? ActionBar.DISPLAY_USE_LOGO : 0, ActionBar.DISPLAY_USE_LOGO); + } + + @Override + public void setListNavigationCallbacks(SpinnerAdapter adapter, ActionBar.OnNavigationListener callback) { + mActionView.setDropdownAdapter(adapter); + mActionView.setCallback(callback); + } + + @Override + public void setNavigationMode(int mode) { + mActionView.setNavigationMode(mode); + } + + @Override + public void setSelectedNavigationItem(int position) { + switch (mActionView.getNavigationMode()) { + default: + case ActionBar.NAVIGATION_MODE_STANDARD: + throw new IllegalStateException(); + + case ActionBar.NAVIGATION_MODE_TABS: + mActionView.getTabAt(position).select(); + break; + + case ActionBar.NAVIGATION_MODE_LIST: + mActionView.setDropdownSelectedPosition(position); + break; + } + } + + @Override + public void selectTab(ActionBar.Tab tab) { + mActionView.selectTab(tab); + } + + @Override + public void setSubtitle(CharSequence subtitle) { + mActionView.setSubtitle(subtitle); + } + + @Override + public void setSubtitle(int resId) { + mActionView.setSubtitle(resId); + } + + @Override + public void setTitle(CharSequence title) { + mActionView.setTitle(title); + } + @Override + public void setTitle(int resId) { + mActionView.setTitle(resId); + } + + @Override + public void show() { + if (mContainerView.getVisibility() != View.VISIBLE) { + mContainerView.startAnimation(mFadeInAnimation); + mContainerView.setVisibility(View.VISIBLE); + } + } +} diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java new file mode 100644 index 000000000..e35064739 --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java @@ -0,0 +1,591 @@ +/* + * Copyright 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.internal.app; + +import java.util.HashMap; +import com.actionbarsherlock.internal.view.menu.MenuItemWrapper; +import com.actionbarsherlock.internal.view.menu.MenuWrapper; +import android.app.Activity; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.v4.app.ActionBar; +import android.support.v4.view.ActionMode; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuInflater; +import android.view.View; +import android.widget.SpinnerAdapter; + +public final class ActionBarWrapper { + //No instances + private ActionBarWrapper() {} + + /** + * Abstraction to get an instance of our implementing class. + * + * @param activity Parent activity. + * @return {@code ActionBar} instance. + */ + public static ActionBar createFor(Activity activity) { + if (!(activity instanceof SherlockActivity)) { + throw new RuntimeException("Activity must implement the SherlockActivity interface"); + } + + return new ActionBarWrapper.Impl(activity); + } + + /** + * Handler for Android's native {@link android.app.ActionBar}. + */ + public static final class Impl extends ActionBar implements android.app.ActionBar.TabListener { + /** Mapping between support listeners and native listeners. */ + private final HashMap mMenuListenerMap = new HashMap(); + + private final Activity mActivity; + + private Impl(Activity activity) { + mActivity = activity; + } + + + /** + * Get the native {@link ActionBar} instance. + * + * @return The action bar. + */ + private android.app.ActionBar getActionBar() { + return mActivity.getActionBar(); + } + + @Override + protected ActionBar getPublicInstance() { + return (getActionBar() != null) ? this : null; + } + + /** + * Converts our Tab wrapper to a native version containing the wrapper + * instance as its tag. + * + * @param tab Tab wrapper instance. + * @return Native tab. + */ + private android.app.ActionBar.Tab convertTabToNative(ActionBar.Tab tab) { + return getActionBar().newTab() + .setCustomView(tab.getCustomView()) + .setIcon(tab.getIcon()) + .setTabListener(this) + .setTag(tab) + .setText(tab.getText()); + } + + @Override + public void onTabReselected(android.app.ActionBar.Tab tab, android.app.FragmentTransaction ft) { + ActionBar.TabListener listener = ((ActionBar.Tab)tab.getTag()).getTabListener(); + if (listener != null) { + listener.onTabReselected((ActionBar.Tab)tab.getTag(), null); + } + } + + @Override + public void onTabSelected(android.app.ActionBar.Tab tab, android.app.FragmentTransaction ft) { + ActionBar.TabListener listener = ((ActionBar.Tab)tab.getTag()).getTabListener(); + if (listener != null) { + listener.onTabSelected((ActionBar.Tab)tab.getTag(), null); + } + } + + @Override + public void onTabUnselected(android.app.ActionBar.Tab tab, android.app.FragmentTransaction ft) { + ActionBar.TabListener listener = ((ActionBar.Tab)tab.getTag()).getTabListener(); + if (listener != null) { + listener.onTabUnselected((ActionBar.Tab)tab.getTag(), null); + } + } + + // --------------------------------------------------------------------- + // ACTION MODE SUPPORT + // --------------------------------------------------------------------- + + @Override + protected ActionMode startActionMode(final ActionMode.Callback callback) { + //We have to re-wrap the instances in every callback since the + //wrapped instance is needed before we could have a change to + //properly store it. + return new ActionModeWrapper(mActivity, + mActivity.startActionMode(new android.view.ActionMode.Callback() { + @Override + public boolean onPrepareActionMode(android.view.ActionMode mode, android.view.Menu menu) { + return callback.onPrepareActionMode(new ActionModeWrapper(mActivity, mode), new MenuWrapper(menu)); + } + + @Override + public void onDestroyActionMode(android.view.ActionMode mode) { + final ActionMode actionMode = new ActionModeWrapper(mActivity, mode); + callback.onDestroyActionMode(actionMode); + + //Send the activity callback once the action mode callback has run. + //This type-check has already occurred in the action bar constructor. + ((SherlockActivity)mActivity).onActionModeFinished(actionMode); + } + + @Override + public boolean onCreateActionMode(android.view.ActionMode mode, android.view.Menu menu) { + return callback.onCreateActionMode(new ActionModeWrapper(mActivity, mode), new MenuWrapper(menu)); + } + + @Override + public boolean onActionItemClicked(android.view.ActionMode mode, android.view.MenuItem item) { + return callback.onActionItemClicked(new ActionModeWrapper(mActivity, mode), new MenuItemWrapper(item)); + } + }) + ); + } + + private static class ActionModeWrapper extends ActionMode { + private final Context mContext; + private final android.view.ActionMode mActionMode; + + ActionModeWrapper(Context context, android.view.ActionMode actionMode) { + mContext = context; + mActionMode = actionMode; + } + + @Override + public void finish() { + mActionMode.finish(); + } + + @Override + public View getCustomView() { + return mActionMode.getCustomView(); + } + + @Override + public Menu getMenu() { + return new MenuWrapper(mActionMode.getMenu()); + } + + @Override + public MenuInflater getMenuInflater() { + return new MenuInflater(mContext, null); + } + + @Override + public CharSequence getSubtitle() { + return mActionMode.getSubtitle(); + } + + @Override + public CharSequence getTitle() { + return mActionMode.getTitle(); + } + + @Override + public void invalidate() { + mActionMode.invalidate(); + } + + @Override + public void setCustomView(View view) { + mActionMode.setCustomView(view); + } + + @Override + public void setSubtitle(int resId) { + mActionMode.setSubtitle(resId); + } + + @Override + public void setSubtitle(CharSequence subtitle) { + mActionMode.setSubtitle(subtitle); + } + + @Override + public void setTitle(int resId) { + mActionMode.setTitle(resId); + } + + @Override + public void setTitle(CharSequence title) { + mActionMode.setTitle(title); + } + } + + // --------------------------------------------------------------------- + // ACTION BAR SUPPORT + // --------------------------------------------------------------------- + + private static class TabImpl extends ActionBar.Tab { + final ActionBarWrapper.Impl mActionBar; + + View mCustomView; + Drawable mIcon; + ActionBar.TabListener mListener; + Object mTag; + CharSequence mText; + + TabImpl(ActionBarWrapper.Impl actionBar) { + mActionBar = actionBar; + } + + @Override + public View getCustomView() { + return mCustomView; + } + + @Override + public Drawable getIcon() { + return mIcon; + } + + @Override + public int getPosition() { + final int tabCount = mActionBar.getTabCount(); + for (int i = 0; i < tabCount; i++) { + if (mActionBar.getTabAt(i).equals(this)) { + return i; + } + } + return ActionBar.Tab.INVALID_POSITION; + } + + @Override + public ActionBar.TabListener getTabListener() { + return mListener; + } + + @Override + public Object getTag() { + return mTag; + } + + @Override + public CharSequence getText() { + return mText; + } + + @Override + public void select() { + mActionBar.selectTab(this); + } + + @Override + public ActionBar.Tab setCustomView(int layoutResId) { + mCustomView = mActionBar.mActivity.getLayoutInflater().inflate(layoutResId, null); + return this; + } + + @Override + public ActionBar.Tab setCustomView(View view) { + mCustomView = view; + return this; + } + + @Override + public ActionBar.Tab setIcon(Drawable icon) { + mIcon = icon; + return this; + } + + @Override + public ActionBar.Tab setIcon(int resId) { + mIcon = mActionBar.mActivity.getResources().getDrawable(resId); + return this; + } + + @Override + public ActionBar.Tab setTabListener(TabListener listener) { + mListener = listener; + return this; + } + + @Override + public ActionBar.Tab setTag(Object obj) { + mTag = obj; + return this; + } + + @Override + public ActionBar.Tab setText(int resId) { + mText = mActionBar.mActivity.getResources().getString(resId); + return this; + } + + @Override + public ActionBar.Tab setText(CharSequence text) { + mText = text; + return this; + } + } + + @Override + public void addOnMenuVisibilityListener(final OnMenuVisibilityListener listener) { + if ((listener != null) && !mMenuListenerMap.containsKey(listener)) { + android.app.ActionBar.OnMenuVisibilityListener nativeListener = new android.app.ActionBar.OnMenuVisibilityListener() { + @Override + public void onMenuVisibilityChanged(boolean isVisible) { + listener.onMenuVisibilityChanged(isVisible); + } + }; + mMenuListenerMap.put(listener, nativeListener); + + getActionBar().addOnMenuVisibilityListener(nativeListener); + } + } + + @Override + public void addTab(Tab tab) { + getActionBar().addTab(convertTabToNative(tab)); + } + + @Override + public void addTab(Tab tab, boolean setSelected) { + getActionBar().addTab(convertTabToNative(tab), setSelected); + } + + @Override + public void addTab(Tab tab, int position) { + getActionBar().addTab(convertTabToNative(tab), position); + } + + @Override + public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { + getActionBar().addTab(convertTabToNative(tab), position, setSelected); + } + + @Override + public View getCustomView() { + return getActionBar().getCustomView(); + } + + @Override + public int getDisplayOptions() { + return getActionBar().getDisplayOptions(); + } + + @Override + public int getHeight() { + return getActionBar().getHeight(); + } + + @Override + public int getNavigationItemCount() { + return getActionBar().getNavigationItemCount(); + } + + @Override + public int getNavigationMode() { + return getActionBar().getNavigationMode(); + } + + @Override + public int getSelectedNavigationIndex() { + return getActionBar().getSelectedNavigationIndex(); + } + + @Override + public Tab getSelectedTab() { + if (getActionBar().getSelectedTab() != null) { + return (ActionBar.Tab)getActionBar().getSelectedTab().getTag(); + } + return null; + } + + @Override + public CharSequence getSubtitle() { + return getActionBar().getSubtitle(); + } + + @Override + public ActionBar.Tab getTabAt(int index) { + if (getActionBar().getTabAt(index) != null) { + return (Tab)getActionBar().getTabAt(index).getTag(); + } + return null; + } + + @Override + public int getTabCount() { + return getActionBar().getTabCount(); + } + + @Override + public CharSequence getTitle() { + return getActionBar().getTitle(); + } + + @Override + public void hide() { + getActionBar().hide(); + } + + @Override + public boolean isShowing() { + return getActionBar().isShowing(); + } + + @Override + public ActionBar.Tab newTab() { + return new TabImpl(this); + } + + @Override + public void removeAllTabs() { + getActionBar().removeAllTabs(); + } + + @Override + public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) { + if ((listener != null) && mMenuListenerMap.containsKey(listener)) { + getActionBar().removeOnMenuVisibilityListener( + mMenuListenerMap.remove(listener) + ); + } + } + + @Override + public void removeTab(Tab tab) { + final int tabCount = getActionBar().getTabCount(); + for (int i = 0; i < tabCount; i++) { + if (getActionBar().getTabAt(i).getTag().equals(tab)) { + getActionBar().removeTabAt(i); + break; + } + } + } + + @Override + public void removeTabAt(int position) { + getActionBar().removeTabAt(position); + } + + @Override + public void selectTab(ActionBar.Tab tab) { + final int tabCount = getActionBar().getTabCount(); + for (int i = 0; i < tabCount; i++) { + if (getActionBar().getTabAt(i).getTag().equals(tab)) { + getActionBar().setSelectedNavigationItem(i); + break; + } + } + } + + @Override + public void setBackgroundDrawable(Drawable d) { + getActionBar().setBackgroundDrawable(d); + } + + @Override + public void setCustomView(int resId) { + getActionBar().setCustomView(resId); + } + + @Override + public void setCustomView(View view) { + getActionBar().setCustomView(view); + } + + @Override + public void setCustomView(View view, LayoutParams layoutParams) { + android.app.ActionBar.LayoutParams nativeLayoutParams = new android.app.ActionBar.LayoutParams(layoutParams); + nativeLayoutParams.gravity = layoutParams.gravity; + getActionBar().setCustomView(view, nativeLayoutParams); + } + + @Override + public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) { + getActionBar().setDisplayHomeAsUpEnabled(showHomeAsUp); + } + + @Override + public void setDisplayOptions(int options, int mask) { + getActionBar().setDisplayOptions(options, mask); + } + + @Override + public void setDisplayOptions(int options) { + getActionBar().setDisplayOptions(options); + } + + @Override + public void setDisplayShowCustomEnabled(boolean showCustom) { + getActionBar().setDisplayShowCustomEnabled(showCustom); + } + + @Override + public void setDisplayShowHomeEnabled(boolean showHome) { + getActionBar().setDisplayShowHomeEnabled(showHome); + } + + @Override + public void setDisplayShowTitleEnabled(boolean showTitle) { + getActionBar().setDisplayShowTitleEnabled(showTitle); + } + + @Override + public void setDisplayUseLogoEnabled(boolean useLogo) { + getActionBar().setDisplayUseLogoEnabled(useLogo); + } + + @Override + public void setListNavigationCallbacks(SpinnerAdapter adapter, final OnNavigationListener callback) { + getActionBar().setListNavigationCallbacks(adapter, new android.app.ActionBar.OnNavigationListener() { + @Override + public boolean onNavigationItemSelected(int itemPosition, long itemId) { + if (callback != null) { + return callback.onNavigationItemSelected(itemPosition, itemId); + } + return false; + } + }); + } + + @Override + public void setNavigationMode(int mode) { + getActionBar().setNavigationMode(mode); + } + + @Override + public void setSelectedNavigationItem(int position) { + getActionBar().setSelectedNavigationItem(position); + } + + @Override + public void setSubtitle(CharSequence subtitle) { + getActionBar().setSubtitle(subtitle); + } + + @Override + public void setSubtitle(int resId) { + getActionBar().setSubtitle(resId); + } + + @Override + public void setTitle(CharSequence title) { + getActionBar().setTitle(title); + } + + @Override + public void setTitle(int resId) { + getActionBar().setTitle(resId); + } + + @Override + public void show() { + getActionBar().show(); + } + } +} diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/app/SherlockActivity.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/app/SherlockActivity.java new file mode 100644 index 000000000..f4bca6377 --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/app/SherlockActivity.java @@ -0,0 +1,13 @@ +package com.actionbarsherlock.internal.app; + +import android.support.v4.view.ActionMode; +import android.support.v4.view.MenuItem; + +/** + * Required callbacks for an activity to work with ActionBarSherlock. + */ +public interface SherlockActivity { + void onActionModeFinished(ActionMode mode); + void onActionModeStarted(ActionMode mode); + boolean onOptionsItemSelected(MenuItem item); +} diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java new file mode 100644 index 000000000..6c32fed30 --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java @@ -0,0 +1,255 @@ +package com.actionbarsherlock.internal.view.menu; + +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.support.v4.view.MenuItem; +import android.support.v4.view.SubMenu; +import android.view.ContextMenu; +import android.view.View; + +public class ActionMenuItem implements MenuItem { + private static final int CHECKABLE = MenuItemImpl.CHECKABLE; + private static final int CHECKED = MenuItemImpl.CHECKED; + private static final int ENABLED = MenuItemImpl.ENABLED; + private static final int EXCLUSIVE = MenuItemImpl.EXCLUSIVE; + private static final int HIDDEN = MenuItemImpl.HIDDEN; + private static final int NO_ICON = 0; + + //XXX UNUSED: private final int mCategoryOrder; + private MenuItem.OnMenuItemClickListener mClickListener; + private Context mContext; + private int mFlags = ENABLED; + private final int mGroup; + private Drawable mIconDrawable; + private int mIconResId = NO_ICON; + private final int mId; + private Intent mIntent; + private final int mOrdering; + private char mShortcutAlphabeticChar; + private char mShortcutNumericChar; + private CharSequence mTitle; + private CharSequence mTitleCondensed; + + public ActionMenuItem(Context context, int group, int id, int categoryOrder, int ordering, CharSequence title) { + mContext = context; + mId = id; + mGroup = group; + //XXX UNUSED mCategoryOrder = categoryOrder; + mOrdering = ordering; + mTitle = title; + } + + @Override + public View getActionView() { + return null; + } + + @Override + public char getAlphabeticShortcut() { + return mShortcutAlphabeticChar; + } + + @Override + public int getGroupId() { + return mGroup; + } + + @Override + public Drawable getIcon() { + return mIconDrawable; + } + + @Override + public Intent getIntent() { + return mIntent; + } + + @Override + public int getItemId() { + return mId; + } + + @Override + public ContextMenu.ContextMenuInfo getMenuInfo() { + return null; + } + + @Override + public char getNumericShortcut() { + return mShortcutNumericChar; + } + + @Override + public int getOrder() { + return mOrdering; + } + + @Override + public SubMenu getSubMenu() { + return null; + } + + @Override + public CharSequence getTitle() { + return mTitle; + } + + @Override + public CharSequence getTitleCondensed() { + return mTitleCondensed; + } + + @Override + public boolean hasSubMenu() { + return false; + } + + public boolean invoke() { + if ((mClickListener != null) && mClickListener.onMenuItemClick(this)) { + return true; + } else if (mIntent != null) { + mContext.startActivity(mIntent); + return true; + } + return false; + } + + @Override + public boolean isCheckable() { + return (mFlags & CHECKABLE) != 0; + } + + @Override + public boolean isChecked() { + return (mFlags & CHECKED) != 0; + } + + @Override + public boolean isEnabled() { + return (mFlags & ENABLED) != 0; + } + + @Override + public boolean isVisible() { + return (mFlags & HIDDEN) == 0; + } + + @Override + public MenuItem setActionView(int layoutResId) { + throw new UnsupportedOperationException(); + } + + @Override + public MenuItem setActionView(View view) { + throw new UnsupportedOperationException(); + } + + @Override + public MenuItem setAlphabeticShortcut(char shortcut) { + mShortcutAlphabeticChar = shortcut; + return this; + } + + @Override + public MenuItem setCheckable(boolean checkable) { + mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0); + return this; + } + + @Override + public MenuItem setChecked(boolean checked) { + mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0); + return this; + } + + @Override + public MenuItem setEnabled(boolean enabled) { + mFlags = (mFlags & ~ENABLED) | (enabled ? ENABLED : 0); + return this; + } + + public ActionMenuItem setExclusiveCheckable(boolean exclusive) { + mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0); + return this; + } + + @Override + public MenuItem setIcon(int resId) { + mIconResId = resId; + mIconDrawable = mContext.getResources().getDrawable(mIconResId); + return this; + } + + @Override + public MenuItem setIcon(Drawable icon) { + mIconDrawable = icon; + mIconResId = NO_ICON; + return this; + } + + @Override + public MenuItem setIntent(Intent intent) { + mIntent = intent; + return this; + } + + @Override + public MenuItem setNumericShortcut(char shortcut) { + mShortcutNumericChar = shortcut; + return this; + } + + @Override + public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener listener) { + mClickListener = listener; + return this; + } + + @Override + public android.view.MenuItem setOnMenuItemClickListener(final android.view.MenuItem.OnMenuItemClickListener listener) { + mClickListener = new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + return listener.onMenuItemClick(item); + } + }; + return null; + } + + @Override + public MenuItem setShortcut(char numericShortcut, char alphabeticShortcut) { + mShortcutNumericChar = numericShortcut; + mShortcutAlphabeticChar = alphabeticShortcut; + return this; + } + + @Override + public void setShowAsAction(int layoutResId) { + //No op + } + + @Override + public MenuItem setTitle(int resId) { + this.mTitle = mContext.getResources().getString(resId); + return this; + } + + @Override + public MenuItem setTitle(CharSequence title) { + mTitle = title; + return this; + } + + @Override + public MenuItem setTitleCondensed(CharSequence title) { + mTitleCondensed = title; + return this; + } + + @Override + public MenuItem setVisible(boolean visible) { + mFlags = (mFlags & HIDDEN) | (visible ? 0 : HIDDEN); + return this; + } +} \ No newline at end of file diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java new file mode 100644 index 000000000..fa6f8bd56 --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java @@ -0,0 +1,149 @@ +package com.actionbarsherlock.internal.view.menu; + +import java.lang.ref.WeakReference; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; +import com.actionbarsherlock.R; + +public class ActionMenuItemView extends RelativeLayout implements MenuView.ItemView, View.OnClickListener { + private ImageView mImageButton; + private TextView mTextButton; + private FrameLayout mCustomView; + private MenuItemImpl mMenuItem; + private WeakReference mDivider; + + public ActionMenuItemView(Context context) { + this(context, null); + } + public ActionMenuItemView(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.actionButtonStyle); + } + public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setOnClickListener(this); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + mImageButton = (ImageView) findViewById(R.id.abs__item_icon); + mImageButton.setOnClickListener(this); + mTextButton = (TextView) findViewById(R.id.abs__item_text); + mTextButton.setOnClickListener(this); + mCustomView = (FrameLayout) findViewById(R.id.abs__item_custom); + mCustomView.setOnClickListener(this); + } + + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + mImageButton.setEnabled(enabled); + mTextButton.setEnabled(enabled); + mCustomView.setEnabled(enabled); + } + + public void setDivider(ImageView divider) { + mDivider = new WeakReference(divider); + //Ensure we are not displaying the divider when we are not visible + setDividerVisibility(getVisibility()); + } + + public void setVisible(boolean visible) { + final int visibility = visible ? View.VISIBLE : View.GONE; + setDividerVisibility(visibility); + setVisibility(visibility); + } + + private void setDividerVisibility(int visibility) { + if ((mDivider != null) && (mDivider.get() != null)) { + mDivider.get().setVisibility(visibility); + } + } + + public void reloadDisplay() { + final boolean hasCustomView = mCustomView.getChildCount() > 0; + final boolean hasText = mMenuItem.showsActionItemText() && !"".equals(mTextButton.getText()); + + if (hasCustomView) { + mCustomView.setVisibility(View.VISIBLE); + mImageButton.setVisibility(View.GONE); + mTextButton.setVisibility(View.GONE); + } else { + mCustomView.setVisibility(View.GONE); + mImageButton.setVisibility(View.VISIBLE); + mTextButton.setVisibility(hasText ? View.VISIBLE : View.GONE); + } + } + + public void setIcon(Drawable icon) { + mImageButton.setImageDrawable(icon); + } + + public void setTitle(CharSequence title) { + mTextButton.setText(title); + reloadDisplay(); + } + + @Override + public void initialize(MenuItemImpl itemData, int menuType) { + mMenuItem = itemData; + setId(itemData.getItemId()); + setIcon(itemData.getIcon()); + setTitle(itemData.getTitle()); + setEnabled(itemData.isEnabled()); + setActionView(itemData.getActionView()); + setVisible(itemData.isVisible()); + } + + @Override + public MenuItemImpl getItemData() { + return mMenuItem; + } + + @Override + public void setCheckable(boolean checkable) { + // No-op + } + + @Override + public void setChecked(boolean checked) { + // No-op + } + + @Override + public void setShortcut(boolean showShortcut, char shortcutKey) { + // No-op + } + + @Override + public void setActionView(View actionView) { + mCustomView.removeAllViews(); + if (actionView != null) { + mCustomView.addView(actionView); + } + reloadDisplay(); + } + + @Override + public boolean prefersCondensedTitle() { + return true; + } + + @Override + public boolean showsIcon() { + return true; + } + + @Override + public void onClick(View v) { + if (mMenuItem != null) { + mMenuItem.invoke(); + } + } +} diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java new file mode 100644 index 000000000..cb0a7556d --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java @@ -0,0 +1,407 @@ +/* + * 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.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.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.view.KeyEvent; + +/** + * An implementation of the {@link android.view.Menu} interface for use in + * inflating menu XML resources to be added to a third-party action bar. + * + * @author Jake Wharton + * @see com.android.internal.view.menu.MenuBuilder + */ +public class MenuBuilder implements Menu { + private static final int DEFAULT_ITEM_ID = 0; + private static final int DEFAULT_GROUP_ID = 0; + private static final int DEFAULT_ORDER = 0; + + public static final int NUM_TYPES = 2; + public static final int TYPE_ACTION_BAR = 0; + public static final int TYPE_NATIVE = 1; + + /** + * 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; + + private static final int[] CATEGORY_TO_ORDER = new int[] { + 1, /* No category */ + 4, /* CONTAINER */ + 5, /* SYSTEM */ + 3, /* SECONDARY */ + 2, /* ALTERNATIVE */ + 0, /* SELECTED_ALTERNATIVE */ + }; + + + + public interface Callback { + public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item); + } + + + + /** Context used for resolving any resources. */ + private final Context mContext; + + /** Child {@link ActionBarMenuItem} items. */ + private final ArrayList mItems; + + /** Menu callback that will receive various events. */ + private Callback mCallback; + + private boolean mShowsActionItemText; + + + + /** + * Create a new action bar menu. + * + * @param context Context used if resource resolution is required. + */ + public MenuBuilder(Context context) { + this.mContext = context; + this.mItems = new ArrayList(); + } + + + /** + * Adds an item to the menu. The other add methods funnel to this. + * + * @param itemId Unique item ID. + * @param groupId Group ID. + * @param order Order. + * @param title Item title. + * @return MenuItem instance. + */ + private MenuItem addInternal(int itemId, int groupId, int order, CharSequence title) { + final int ordering = getOrdering(order); + final MenuItemImpl item = new MenuItemImpl(this, groupId, itemId, order, ordering, title, MenuItem.SHOW_AS_ACTION_NEVER); + + mItems.add(findInsertIndex(mItems, ordering), item); + return item; + } + + private static int findInsertIndex(ArrayList 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; + } + + /** + * 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 >= CATEGORY_TO_ORDER.length) { + throw new IllegalArgumentException("order does not contain a valid category."); + } + + return (CATEGORY_TO_ORDER[index] << CATEGORY_SHIFT) | (categoryOrder & USER_MASK); + } + + public void setCallback(Callback callback) { + mCallback = callback; + } + + public Callback getCallback() { + return mCallback; + } + + public boolean getShowsActionItemText() { + return mShowsActionItemText; + } + + public void setShowsActionItemText(boolean showsActionItemText) { + mShowsActionItemText = showsActionItemText; + } + + /** + * Gets the root menu (if this is a submenu, find its root menu). + * + * @return The root menu. + */ + public MenuBuilder getRootMenu() { + return this; + } + + /** + * Get a list of the items contained in this menu. + * + * @return List of {@link MenuItemImpl}s. + */ + public final List getItems() { + return this.mItems; + } + + final MenuItemImpl remove(int index) { + return this.mItems.remove(index); + } + + final Context getContext() { + return this.mContext; + } + + 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); + } + } + } + + // ** Menu Methods ** \\ + + @Override + public MenuItem add(int titleResourceId) { + return addInternal(0, 0, 0, mContext.getResources().getString(titleResourceId)); + } + + @Override + public MenuItem add(int groupId, int itemId, int order, int titleResourceId) { + return addInternal(itemId, groupId, order, mContext.getResources().getString(titleResourceId)); + } + + @Override + public MenuItem add(int groupId, int itemId, int order, CharSequence title) { + return addInternal(itemId, groupId, order, title); + } + + @Override + public MenuItem add(CharSequence title) { + return addInternal(0, 0, 0, title); + } + + @Override + public SubMenuBuilder addSubMenu(CharSequence title) { + return this.addSubMenu(DEFAULT_GROUP_ID, DEFAULT_ITEM_ID, DEFAULT_ORDER, title); + } + + @Override + public SubMenuBuilder addSubMenu(int titleResourceId) { + return this.addSubMenu(DEFAULT_GROUP_ID, DEFAULT_ITEM_ID, DEFAULT_ORDER, titleResourceId); + } + + @Override + public SubMenuBuilder addSubMenu(int groupId, int itemId, int order, int titleResourceId) { + String title = this.mContext.getResources().getString(titleResourceId); + return this.addSubMenu(groupId, itemId, order, title); + } + + @Override + public SubMenuBuilder addSubMenu(int groupId, int itemId, int order, CharSequence title) { + MenuItemImpl item = (MenuItemImpl)this.add(groupId, itemId, order, title); + SubMenuBuilder subMenu = new SubMenuBuilder(this.mContext, this, item); + item.setSubMenu(subMenu); + return subMenu; + } + + @Override + public void clear() { + this.mItems.clear(); + } + + @Override + public void close() {} + + @Override + public MenuItemImpl findItem(int itemId) { + for (MenuItemImpl item : this.mItems) { + if (item.getItemId() == itemId) { + return item; + } + } + return null; + } + + @Override + public MenuItemImpl getItem(int index) { + return this.mItems.get(index); + } + + @Override + public boolean hasVisibleItems() { + for (MenuItem item : this.mItems) { + if (item.isVisible()) { + return true; + } + } + return false; + } + + @Override + public void removeItem(int itemId) { + final int size = this.mItems.size(); + for (int i = 0; i < size; i++) { + if (this.mItems.get(i).getItemId() == itemId) { + this.mItems.remove(i); + return; + } + } + } + + @Override + public int size() { + return this.mItems.size(); + } + + @Override + public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, android.view.MenuItem[] outSpecificItems) { + PackageManager pm = mContext.getPackageManager(); + final List 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= 0) { + outSpecificItems[ri.specificIndex] = item; + } + } + + return N; + } + + @Override + public boolean isShortcutKey(int keyCode, KeyEvent event) { + return false; + } + + @Override + public boolean performIdentifierAction(int id, int flags) { + throw new RuntimeException("Method not supported."); + } + + @Override + public boolean performShortcut(int keyCode, KeyEvent event, int flags) { + return false; + } + + @Override + public void removeGroup(int groupId) { + for (int i = mItems.size() - 1; i > 0; i--) { + if (mItems.get(i).getGroupId() == groupId) { + mItems.remove(i); + } + } + } + + @Override + public void setGroupCheckable(int groupId, boolean checkable, boolean exclusive) { + final int N = mItems.size(); + for (int i = 0; i < N; i++) { + MenuItemImpl item = mItems.get(i); + if (item.getGroupId() == groupId) { + item.setExclusiveCheckable(exclusive); + item.setCheckable(checkable); + } + } + } + + @Override + public void setGroupEnabled(int groupId, boolean enabled) { + final int size = this.mItems.size(); + for (int i = 0; i < size; i++) { + MenuItemImpl item = mItems.get(i); + if (item.getGroupId() == groupId) { + item.setEnabled(enabled); + } + } + } + + @Override + public void setGroupVisible(int groupId, boolean visible) { + final int size = this.mItems.size(); + for (int i = 0; i < size; i++) { + MenuItemImpl item = mItems.get(i); + if (item.getGroupId() == groupId) { + item.setVisible(visible); + } + } + } + + @Override + public void setQwertyMode(boolean isQwerty) { + throw new RuntimeException("Method not supported."); + } +} diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuInflaterWrapper.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuInflaterWrapper.java new file mode 100644 index 000000000..29040bab5 --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuInflaterWrapper.java @@ -0,0 +1,21 @@ +package com.actionbarsherlock.internal.view.menu; + +import android.content.Context; + +public final class MenuInflaterWrapper extends android.view.MenuInflater { + private final android.view.MenuInflater mMenuInflater; + + public MenuInflaterWrapper(Context context, android.view.MenuInflater menuInflater) { + super(context); + mMenuInflater = menuInflater; + } + + @Override + public void inflate(int menuRes, android.view.Menu menu) { + if (menu instanceof MenuWrapper) { + mMenuInflater.inflate(menuRes, ((MenuWrapper)menu).unwrap()); + } else { + mMenuInflater.inflate(menuRes, menu); + } + } +} diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java new file mode 100644 index 000000000..9e2b7c704 --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java @@ -0,0 +1,663 @@ +/* + * 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.internal.view.menu; + +import java.lang.ref.WeakReference; +import android.app.AlertDialog; +import android.content.ActivityNotFoundException; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.support.v4.view.MenuItem; +import android.util.Log; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.View; + +/** + * An implementation of the {@link android.view.MenuItem} interface for use in + * inflating menu XML resources to be added to a third-party action bar. + * + * @author Jake Wharton + * @see com.android.internal.view.menu.MenuItemImpl + */ +public final class MenuItemImpl implements MenuItem { + private static final String TAG = "MenuItemImpl"; + + private final MenuBuilder mMenu; + + private final int mItemId; + private final int mGroupId; + private final int mCategoryOrder; + private final int mOrdering; + + private Intent mIntent; + private CharSequence mTitle; + private CharSequence mTitleCondensed; + private char mNumericalShortcut; + private char mAlphabeticalShortcut; + private int mShowAsAction; + private SubMenuBuilder mSubMenu; + private Runnable mItemCallback; + private OnMenuItemClickListener mClickListener; + private Drawable mIcon; + private int mIconRes = View.NO_ID; + private View mActionView; + private int mActionViewRes = View.NO_ID; + + int mFlags = ENABLED; + static final int CHECKABLE = 0x01; + static final int CHECKED = 0x02; + static final int EXCLUSIVE = 0x04; + static final int HIDDEN = 0x08; + static final int ENABLED = 0x10; + static final int IS_ACTION = 0x20; + + private final WeakReference[] mItemViews; + + private final DialogInterface.OnClickListener subMenuClick = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int index) { + dialog.dismiss(); + mSubMenu.getItem(index).invoke(); + } + }; + private final DialogInterface.OnMultiChoiceClickListener subMenuMultiClick = new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialog, int index, boolean isChecked) { + dialog.dismiss(); + mSubMenu.getItem(index).setChecked(isChecked); + } + }; + + + /** + * Create a new action bar menu item. + * + * @param context Context used if resource resolution is required. + * @param itemId A unique ID. Used in the activity callback. + * @param groupId Group ID. Currently unused. + * @param order Item order. Currently unused. + * @param title Title of the item. + */ + @SuppressWarnings("unchecked") + public MenuItemImpl(MenuBuilder menu, int groupId, int itemId, int order, int ordering, CharSequence title, int showAsAction) { + mMenu = menu; + + mItemId = itemId; + mGroupId = groupId; + mCategoryOrder = order; + mOrdering = ordering; + mTitle = title; + mShowAsAction = showAsAction; + + mItemViews = new WeakReference[MenuBuilder.NUM_TYPES]; + } + + + + public boolean invoke() { + if (hasSubMenu()) { + AlertDialog.Builder builder = new AlertDialog.Builder(mMenu.getContext()); + builder.setTitle(getTitle()); + + final boolean isExclusive = mSubMenu.getItem(0).isExclusiveCheckable(); + final boolean isCheckable = mSubMenu.getItem(0).isCheckable(); + final CharSequence[] titles = getSubMenuTitles(); + if (isExclusive) { + builder.setSingleChoiceItems(titles, getSubMenuSelected(), subMenuClick); + } else if (isCheckable) { + builder.setMultiChoiceItems(titles, getSubMenuChecked(), subMenuMultiClick); + } else { + builder.setItems(titles, subMenuClick); + } + + builder.show(); + return true; + } + + if (mClickListener != null && + mClickListener.onMenuItemClick(this)) { + return true; + } + + MenuBuilder.Callback callback = mMenu.getRootMenu().getCallback(); + if (callback != null && + callback.onMenuItemSelected(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); + } + } + + return false; + } + + private CharSequence[] getSubMenuTitles() { + final int count = mSubMenu.size(); + CharSequence[] list = new CharSequence[count]; + for (int i = 0; i < count; i++) { + list[i] = mSubMenu.getItem(i).getTitle(); + } + return list; + } + + private int getSubMenuSelected() { + final int count = mSubMenu.size(); + for (int i = 0; i < count; i++) { + if (mSubMenu.getItem(i).isChecked()) { + return i; + } + } + return -1; + } + + private boolean[] getSubMenuChecked() { + final int count = mSubMenu.size(); + boolean[] checked = new boolean[count]; + for (int i = 0; i < count; i++) { + checked[i] = mSubMenu.getItem(i).isChecked(); + } + return checked; + } + + private boolean hasItemView(int menuType) { + return mItemViews[menuType] != null && mItemViews[menuType].get() != null; + } + + public void setItemView(int type, MenuView.ItemView itemView) { + mItemViews[type] = new WeakReference(itemView); + } + + + public void addTo(android.view.Menu menu) { + if (hasSubMenu()) { + android.view.SubMenu subMenu = menu.addSubMenu(mGroupId, mItemId, mCategoryOrder, mTitle); + if (mIconRes != View.NO_ID) { + subMenu.setIcon(mIconRes); + } else { + subMenu.setIcon(mIcon); + } + for (MenuItemImpl item : mSubMenu.getItems()) { + item.addTo(subMenu); + } + + if (mSubMenu.getItem(0).isExclusiveCheckable()) { + int checked = getSubMenuSelected(); + if (checked != -1) { + subMenu.getItem(checked).setChecked(true); + } + } + } else { + android.view.MenuItem item = menu.add(mGroupId, mItemId, mCategoryOrder, mTitle) + .setAlphabeticShortcut(mAlphabeticalShortcut) + .setNumericShortcut(mNumericalShortcut) + .setVisible(isVisible()) + .setIntent(mIntent) + .setCheckable(isCheckable()) + .setChecked(isChecked()) + .setOnMenuItemClickListener(mClickListener); + + if (isExclusiveCheckable()) { + menu.setGroupCheckable(mGroupId, true, true); + } + + //Create and initialize a native itemview wrapper + NativeMenuItemView nativeWrapper = new NativeMenuItemView(item); + nativeWrapper.initialize(this, MenuBuilder.TYPE_NATIVE); + + //Associate the itemview to this so changes will be reflected + setItemView(MenuBuilder.TYPE_NATIVE, nativeWrapper); + } + } + + /** + * Get whether or not this item is being shown on the action bar. + * + * @return {@code true} if shown, {@code false} otherwise. + */ + public boolean isShownOnActionBar() { + return (mFlags & IS_ACTION) == IS_ACTION; + } + + /** + * Denote whether or not this menu item is being shown on the action bar. + * + * @param isShownOnActionBar {@code true} if shown or {@code false}. + */ + public void setIsShownOnActionBar(boolean isShownOnActionBar) { + mFlags = (mFlags & ~IS_ACTION) | (isShownOnActionBar ? IS_ACTION : 0); + } + + @Override + public Intent getIntent() { + return this.mIntent; + } + + @Override + public int getItemId() { + return this.mItemId; + } + + @Override + public CharSequence getTitle() { + return this.mTitle; + } + + @Override + public boolean isEnabled() { + return (mFlags & ENABLED) != 0; + } + + @Override + public boolean isVisible() { + return (mFlags & HIDDEN) == 0; + } + + @Override + public MenuItem setEnabled(boolean enabled) { + final boolean oldValue = isEnabled(); + mFlags = (mFlags & ~ENABLED) | (enabled ? ENABLED : 0); + + if (oldValue != enabled) { + for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) { + if (hasItemView(i)) { + mItemViews[i].get().setEnabled(enabled); + } + } + } + + return this; + } + + @Override + public MenuItem setIcon(int iconResourceId) { + mIcon = null; + mIconRes = iconResourceId; + + if (mIconRes != View.NO_ID) { + setIconOnViews(mMenu.getContext().getResources().getDrawable(mIconRes)); + } + + return this; + } + + @Override + public MenuItem setIntent(Intent intent) { + mIntent = intent; + return this; + } + + @Override + public MenuItem setTitle(CharSequence title) { + mTitle = title; + return this; + } + + @Override + public MenuItem setTitle(int titleResourceId) { + mTitle = mMenu.getContext().getResources().getString(titleResourceId); + return this; + } + + @Override + public MenuItem setVisible(boolean visible) { + final boolean oldValue = isVisible(); + mFlags = (mFlags & ~HIDDEN) | (visible ? 0 : HIDDEN); + if (oldValue != visible) { + for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) { + if (hasItemView(i)) { + mItemViews[i].get().setVisible(visible); + } + } + } + return this; + } + + @Override + public boolean isChecked() { + return (mFlags & CHECKED) == CHECKED; + } + + @Override + public MenuItem setChecked(boolean checked) { + if ((mFlags & EXCLUSIVE) == EXCLUSIVE) { + // 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 boolean oldValue = isChecked(); + mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0); + if (oldValue != checked) { + for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) { + if (hasItemView(i)) { + mItemViews[i].get().setChecked(checked); + } + } + } + } + + @Override + public boolean isCheckable() { + return (mFlags & CHECKABLE) == CHECKABLE; + } + + @Override + public MenuItem setCheckable(boolean checkable) { + final boolean oldValue = isCheckable(); + mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0); + if (oldValue != checkable) { + for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) { + if (hasItemView(i)) { + mItemViews[i].get().setCheckable(checkable); + } + } + } + + return this; + } + + public void setExclusiveCheckable(boolean exclusive) { + mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0); + } + + public boolean isExclusiveCheckable() { + return (mFlags & EXCLUSIVE) == EXCLUSIVE; + } + + @Override + public CharSequence getTitleCondensed() { + return mTitleCondensed; + } + + @Override + public MenuItem setTitleCondensed(CharSequence title) { + mTitleCondensed = title; + return this; + } + + @Override + public int getGroupId() { + return mGroupId; + } + + @Override + public int getOrder() { + return mCategoryOrder; + } + + public int getOrdering() { + return mOrdering; + } + + @Override + public SubMenuBuilder getSubMenu() { + return mSubMenu; + } + + /** + * Set the sub-menu of this item. + * + * @param subMenu Sub-menu instance. + * @return This Item so additional setters can be called. + */ + MenuItem setSubMenu(SubMenuBuilder subMenu) { + mSubMenu = subMenu; + return this; + } + + @Override + public boolean hasSubMenu() { + return (mSubMenu != null) && (mSubMenu.size() > 0); + } + + @Override + public char getAlphabeticShortcut() { + return mAlphabeticalShortcut; + } + + @Override + public char getNumericShortcut() { + return mNumericalShortcut; + } + + @Override + public MenuItem setAlphabeticShortcut(char alphaChar) { + mAlphabeticalShortcut = Character.toLowerCase(alphaChar); + return this; + } + + @Override + public MenuItem setNumericShortcut(char numericChar) { + mNumericalShortcut = numericChar; + return this; + } + + @Override + public MenuItem setShortcut(char numericChar, char alphaChar) { + setNumericShortcut(numericChar); + setAlphabeticShortcut(alphaChar); + return this; + } + + @Override + public void setShowAsAction(int actionEnum) { + mShowAsAction = actionEnum; + } + + public int getShowAsAction() { + return mShowAsAction; + } + + public boolean showsActionItemText() { + return mMenu.getShowsActionItemText(); + } + + @Override + public View getActionView() { + if (mActionView != null) { + return mActionView; + } + if (mActionViewRes != View.NO_ID) { + return LayoutInflater.from(mMenu.getContext()).inflate(mActionViewRes, null, false); + } + return null; + } + + @Override + public Drawable getIcon() { + if (mIcon != null) { + return mIcon; + } + if (mIconRes != View.NO_ID) { + return mMenu.getContext().getResources().getDrawable(mIconRes); + } + return null; + } + + @Override + public ContextMenuInfo getMenuInfo() { + return null; + } + + @Override + public MenuItem setActionView(View view) { + mActionView = view; + mActionViewRes = View.NO_ID; + setActionViewOnViews(mActionView); + return this; + } + + @Override + public MenuItem setActionView(int resId) { + mActionView = null; + mActionViewRes = resId; + + if (mActionViewRes != View.NO_ID) { + setActionViewOnViews(LayoutInflater.from(mMenu.getContext()).inflate(mActionViewRes, null, false)); + } + + return this; + } + + void setActionViewOnViews(View view) { + for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) { + if (hasItemView(i)) { + mItemViews[i].get().setActionView(view); + } + } + } + + @Override + public MenuItem setIcon(Drawable icon) { + mIcon = icon; + mIconRes = View.NO_ID; + setIconOnViews(icon); + return this; + } + + void setIconOnViews(Drawable icon) { + for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) { + if (hasItemView(i)) { + mItemViews[i].get().setIcon(icon); + } + } + } + + @Override + public android.view.MenuItem setOnMenuItemClickListener(final android.view.MenuItem.OnMenuItemClickListener menuItemClickListener) { + return this.setOnMenuItemClickListener(new OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + return menuItemClickListener.onMenuItemClick(new MenuItemWrapper(item)); + } + }); + } + + @Override + public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) { + mClickListener = menuItemClickListener; + return this; + } + + /** + * Returns the currently set menu click listener for this item. + * + * @return Click listener or {@code null}. + */ + public OnMenuItemClickListener getOnMenuItemClickListener() { + return mClickListener; + } + + + + + public static final class NativeMenuItemView implements MenuView.ItemView { + private final android.view.MenuItem mItem; + + + public NativeMenuItemView(android.view.MenuItem item) { + mItem = item; + } + + + @Override + public MenuItemImpl getItemData() { + return null; + } + + @Override + public void initialize(MenuItemImpl itemData, int menuType) { + setIcon(itemData.getIcon()); + setTitle(itemData.getTitle()); + setEnabled(itemData.isEnabled()); + setCheckable(itemData.isCheckable()); + setChecked(itemData.isChecked()); + setActionView(itemData.getActionView()); + setVisible(itemData.isVisible()); + } + + @Override + public boolean prefersCondensedTitle() { + return true; + } + + @Override + public void setCheckable(boolean checkable) { + mItem.setCheckable(checkable); + } + + @Override + public void setChecked(boolean checked) { + mItem.setChecked(checked); + } + + @Override + public void setEnabled(boolean enabled) { + mItem.setEnabled(enabled); + } + + @Override + public void setIcon(Drawable icon) { + mItem.setIcon(icon); + } + + @Override + public void setShortcut(boolean showShortcut, char shortcutKey) { + //Not supported + } + + @Override + public void setTitle(CharSequence title) { + mItem.setTitle(title); + } + + @Override + public boolean showsIcon() { + return true; + } + + @Override + public void setActionView(View actionView) { + //Not supported + } + + @Override + public void setVisible(boolean visible) { + mItem.setVisible(visible); + } + } +} \ No newline at end of file diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java new file mode 100644 index 000000000..2ed1e548e --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java @@ -0,0 +1,315 @@ +/* + * 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.internal.view.menu; + +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.support.v4.view.MenuItem; +import android.support.v4.view.SubMenu; +import android.view.View; +import android.view.ContextMenu.ContextMenuInfo; + +/** + *

Interface for direct access to a previously created menu item.

+ * + *

An Item is returned by calling one of the {@link Menu#add(int)} + * methods.

+ * + *

For a feature set of specific menu types, see {@link Menu}.

+ */ +public final class MenuItemWrapper implements MenuItem { + private static final class HoneycombMenuItem { + static View getActionView(android.view.MenuItem item) { + return item.getActionView(); + } + + static void setActionView(android.view.MenuItem item, int resId) { + item.setActionView(resId); + } + + static void setActionView(android.view.MenuItem item, View view) { + item.setActionView(view); + } + + static void setShowAsAction(android.view.MenuItem item, int actionEnum) { + item.setShowAsAction(actionEnum); + } + } + + /** Native {@link android.view.MenuItem} whose methods are wrapped. */ + private final android.view.MenuItem mMenuItem; + + /** + * Constructor used to create a wrapper to a native + * {@link android.view.MenuItem} so we can return the same type for native + * and {@link MenuItemImpl} instances, the latter of which will override + * all the methods defined in this base class. + * + * @param menuItem Native instance. + */ + public MenuItemWrapper(android.view.MenuItem menuItem) { + mMenuItem = menuItem; + } + + + /** + * Returns the currently set action view for this menu item. + * + * @return The item's action view + * @see #setActionView(int) + * @see #setActionView(View) + * @see #setShowAsAction(int) + */ + public View getActionView() { + if (mMenuItem != null) { + return HoneycombMenuItem.getActionView(mMenuItem); + } + return null; + } + + /** + * 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. + * + * @param resId Layout resource to use for presenting this item to the user. + * @return This Item so additional setters can be called. + * @see #setActionView(View) + */ + public MenuItem setActionView(int resId) { + if (mMenuItem != null) { + HoneycombMenuItem.setActionView(mMenuItem, resId); + } + return this; + } + + /** + * 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. + * + * @param view View to use for presenting this item to the user. + * @return This Item so additional setters can be called. + * @see #setActionView(int) + */ + public MenuItem setActionView(View view) { + if (mMenuItem != null) { + HoneycombMenuItem.setActionView(mMenuItem, view); + } + return this; + } + + /** + * 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}. + * {@link #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}. {@link #SHOW_AS_ACTION_NEVER} is the + * default. + */ + public void setShowAsAction(int actionEnum) { + if (mMenuItem != null) { + HoneycombMenuItem.setShowAsAction(mMenuItem, actionEnum); + } + } + + // --------------------------------------------------------------------- + // MENU ITEM SUPPORT + // --------------------------------------------------------------------- + + @Override + public char getAlphabeticShortcut() { + return mMenuItem.getAlphabeticShortcut(); + } + + @Override + public int getGroupId() { + return mMenuItem.getGroupId(); + } + + @Override + public Drawable getIcon() { + return mMenuItem.getIcon(); + } + + @Override + public Intent getIntent() { + return mMenuItem.getIntent(); + } + + @Override + public int getItemId() { + return mMenuItem.getItemId(); + } + + @Override + public ContextMenuInfo getMenuInfo() { + return mMenuItem.getMenuInfo(); + } + + @Override + public char getNumericShortcut() { + return mMenuItem.getNumericShortcut(); + } + + @Override + public int getOrder() { + return mMenuItem.getOrder(); + } + + @Override + public SubMenu getSubMenu() { + return new SubMenuWrapper(mMenuItem.getSubMenu()); + } + + @Override + public CharSequence getTitle() { + return mMenuItem.getTitle(); + } + + @Override + public CharSequence getTitleCondensed() { + return mMenuItem.getTitleCondensed(); + } + + @Override + public boolean hasSubMenu() { + return mMenuItem.hasSubMenu(); + } + + @Override + public boolean isCheckable() { + return mMenuItem.isCheckable(); + } + + @Override + public boolean isChecked() { + return mMenuItem.isChecked(); + } + + @Override + public boolean isEnabled() { + return mMenuItem.isEnabled(); + } + + @Override + public boolean isVisible() { + return mMenuItem.isVisible(); + } + + @Override + public MenuItem setAlphabeticShortcut(char alphaChar) { + mMenuItem.setAlphabeticShortcut(alphaChar); + return this; + } + + @Override + public MenuItem setCheckable(boolean checkable) { + mMenuItem.setCheckable(checkable); + return this; + } + + @Override + public MenuItem setChecked(boolean checked) { + mMenuItem.setChecked(checked); + return this; + } + + @Override + public MenuItem setEnabled(boolean enabled) { + mMenuItem.setEnabled(enabled); + return this; + } + + @Override + public MenuItem setIcon(Drawable icon) { + mMenuItem.setIcon(icon); + return this; + } + + @Override + public MenuItem setIcon(int iconRes) { + mMenuItem.setIcon(iconRes); + return this; + } + + @Override + public MenuItem setIntent(Intent intent) { + mMenuItem.setIntent(intent); + return this; + } + + @Override + public MenuItem setNumericShortcut(char numericChar) { + mMenuItem.setNumericShortcut(numericChar); + return this; + } + + @Override + public MenuItem setOnMenuItemClickListener(android.view.MenuItem.OnMenuItemClickListener menuItemClickListener) { + mMenuItem.setOnMenuItemClickListener(menuItemClickListener); + return this; + } + + /** + * Set a custom listener for invocation of this menu item. + * + * @param menuItemClickListener The object to receive invokations. + * @return This Item so additional setters can be called. + */ + public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) { + mMenuItem.setOnMenuItemClickListener(menuItemClickListener); + return this; + } + + @Override + public MenuItem setShortcut(char numericChar, char alphaChar) { + mMenuItem.setShortcut(numericChar, alphaChar); + return this; + } + + @Override + public MenuItem setTitle(CharSequence title) { + mMenuItem.setTitle(title); + return this; + } + + @Override + public MenuItem setTitle(int title) { + mMenuItem.setTitle(title); + return this; + } + + @Override + public MenuItem setTitleCondensed(CharSequence title) { + mMenuItem.setTitleCondensed(title); + return this; + } + + @Override + public MenuItem setVisible(boolean visible) { + mMenuItem.setVisible(visible); + return this; + } +} diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuView.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuView.java new file mode 100644 index 000000000..daae3fe8c --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuView.java @@ -0,0 +1,143 @@ +/* + * 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; +import android.view.View; + +/** + * Minimal interface for a menu view. {@link #initialize(MenuBuilder, int)} 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. + * @param menuType The type of this menu, one of + * {@link MenuBuilder#TYPE_ICON}, {@link MenuBuilder#TYPE_EXPANDED}, + * {@link MenuBuilder#TYPE_DIALOG}). + */ + public void initialize(MenuBuilder menu, int menuType); + + /** + * Forces the menu view to update its view to reflect the new state of the menu. + * + * @param cleared Whether the menu was cleared or just modified. + */ + public void updateChildren(boolean cleared); + + /** + * 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 visibility for the item view. + * @param visible Whether the item is visible + */ + public void setVisible(boolean visible); + + /** + * 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); + + /** + * Set the action view of this item view. + * @param actionView Action view. + */ + public void setActionView(View actionView); + + /** + * 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(); + } +} \ No newline at end of file diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java new file mode 100644 index 000000000..4e499930d --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java @@ -0,0 +1,156 @@ +package com.actionbarsherlock.internal.view.menu; + +import android.content.ComponentName; +import android.content.Intent; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.support.v4.view.SubMenu; +import android.view.KeyEvent; + +/** + * Wrapper around a native Menu instance which implements our version of the + * Menu interface. + */ +public class MenuWrapper implements Menu { + /** Native menu. */ + private final android.view.Menu mMenu; + + /** + * Create a new wrapped instance. + * + * @param menu Native menu. + */ + public MenuWrapper(android.view.Menu menu) { + this.mMenu = menu; + } + + /** + * Get the native menu instance we are wrapping. + * + * @return Native menu. + */ + android.view.Menu unwrap() { + return mMenu; + } + + @Override + public MenuItem add(CharSequence title) { + return new MenuItemWrapper(mMenu.add(title)); + } + + @Override + public MenuItem add(int groupId, int itemId, int order, int titleRes) { + return new MenuItemWrapper(mMenu.add(groupId, itemId, order, titleRes)); + } + + @Override + public MenuItem add(int titleRes) { + return new MenuItemWrapper(mMenu.add(titleRes)); + } + + @Override + public MenuItem add(int groupId, int itemId, int order, CharSequence title) { + return new MenuItemWrapper(mMenu.add(groupId, itemId, order, title)); + } + + @Override + public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, android.view.MenuItem[] outSpecificItems) { + return mMenu.addIntentOptions(groupId, itemId, order, caller, specifics, intent, flags, outSpecificItems); + } + + @Override + public SubMenu addSubMenu(CharSequence title) { + return new SubMenuWrapper(mMenu.addSubMenu(title)); + } + + @Override + public SubMenu addSubMenu(int titleRes) { + return new SubMenuWrapper(mMenu.addSubMenu(titleRes)); + } + + @Override + public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) { + return new SubMenuWrapper(mMenu.addSubMenu(groupId, itemId, order, title)); + } + + @Override + public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) { + return new SubMenuWrapper(mMenu.addSubMenu(groupId, itemId, order, titleRes)); + } + + @Override + public void clear() { + mMenu.clear(); + } + + @Override + public void close() { + mMenu.close(); + } + + @Override + public MenuItem findItem(int id) { + android.view.MenuItem item = mMenu.findItem(id); + return (item != null) ? new MenuItemWrapper(item) : null; + } + + @Override + public MenuItem getItem(int index) { + return new MenuItemWrapper(mMenu.getItem(index)); + } + + @Override + public boolean hasVisibleItems() { + return mMenu.hasVisibleItems(); + } + + @Override + public boolean isShortcutKey(int keyCode, KeyEvent event) { + return mMenu.isShortcutKey(keyCode, event); + } + + @Override + public boolean performIdentifierAction(int id, int flags) { + return mMenu.performIdentifierAction(id, flags); + } + + @Override + public boolean performShortcut(int keyCode, KeyEvent event, int flags) { + return mMenu.performShortcut(keyCode, event, flags); + } + + @Override + public void removeGroup(int groupId) { + mMenu.removeGroup(groupId); + } + + @Override + public void removeItem(int id) { + mMenu.removeItem(id); + } + + @Override + public void setGroupCheckable(int group, boolean checkable, boolean exclusive) { + mMenu.setGroupCheckable(group, checkable, exclusive); + } + + @Override + public void setGroupEnabled(int group, boolean enabled) { + mMenu.setGroupEnabled(group, enabled); + } + + @Override + public void setGroupVisible(int group, boolean visible) { + mMenu.setGroupVisible(group, visible); + } + + @Override + public void setQwertyMode(boolean isQwerty) { + mMenu.setQwertyMode(isQwerty); + } + + @Override + public int size() { + return mMenu.size(); + } +} diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java new file mode 100644 index 000000000..bf73ed757 --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java @@ -0,0 +1,119 @@ +/* + * 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.internal.view.menu; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.v4.view.SubMenu; +import android.view.View; + +/** + * The model for a sub menu, which is an extension of the menu. Most methods + * are proxied to the parent menu. + */ +public final 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(); + //} + + MenuBuilder getParentMenu() { + return mParentMenu; + } + + @Override + public MenuItemImpl getItem() { + return mItem; + } + + //@Override + //public Callback getCallback() { + // return mParentMenu.getCallback(); + //} + + //@Override + //public void setCallback(Callback callback) { + // mParentMenu.setCallback(callback); + //} + + @Override + public MenuBuilder getRootMenu() { + return mParentMenu; + } + + public SubMenuBuilder setIcon(Drawable icon) { + mItem.setIcon(icon); + return this; + } + + public SubMenuBuilder setIcon(int iconRes) { + mItem.setIcon(iconRes); + return this; + } + + public SubMenuBuilder setHeaderIcon(Drawable icon) { + throw new RuntimeException("Method not supported."); + } + + public SubMenuBuilder setHeaderIcon(int iconRes) { + throw new RuntimeException("Method not supported."); + } + + public SubMenuBuilder setHeaderTitle(CharSequence title) { + throw new RuntimeException("Method not supported."); + } + + public SubMenuBuilder setHeaderTitle(int titleRes) { + throw new RuntimeException("Method not supported."); + } + + @Override + public SubMenuBuilder setHeaderView(View view) { + throw new RuntimeException("Method not supported."); + } + + @Override + public void clearHeader() { + throw new RuntimeException("Method not supported."); + } +} \ No newline at end of file diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java new file mode 100644 index 000000000..ca978e13a --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java @@ -0,0 +1,82 @@ +package com.actionbarsherlock.internal.view.menu; + +import android.graphics.drawable.Drawable; +import android.support.v4.view.MenuItem; +import android.support.v4.view.SubMenu; +import android.view.View; + +public class SubMenuWrapper extends MenuWrapper implements SubMenu { + /** Native sub-menu. */ + private final android.view.SubMenu mSubMenu; + + /** + * Create a new wrapped instance. + * + * @param subMenu Native sub-menu. + */ + public SubMenuWrapper(android.view.SubMenu subMenu) { + super(subMenu); + mSubMenu = subMenu; + } + + /** + * Get the native sub-menu instance we are wrapping. + * + * @return Native sub-menu. + */ + android.view.SubMenu unwrap() { + return mSubMenu; + } + + @Override + public void clearHeader() { + mSubMenu.clearHeader(); + } + + @Override + public MenuItem getItem() { + return new MenuItemWrapper(mSubMenu.getItem()); + } + + @Override + public SubMenu setHeaderIcon(Drawable icon) { + mSubMenu.setHeaderIcon(icon); + return this; + } + + @Override + public SubMenu setHeaderIcon(int iconRes) { + mSubMenu.setHeaderIcon(iconRes); + return this; + } + + @Override + public SubMenu setHeaderTitle(CharSequence title) { + mSubMenu.setHeaderTitle(title); + return this; + } + + @Override + public SubMenu setHeaderTitle(int titleRes) { + mSubMenu.setHeaderTitle(titleRes); + return this; + } + + @Override + public SubMenu setHeaderView(View view) { + mSubMenu.setHeaderView(view); + return this; + } + + @Override + public SubMenu setIcon(Drawable icon) { + mSubMenu.setIcon(icon); + return this; + } + + @Override + public SubMenu setIcon(int iconRes) { + mSubMenu.setIcon(iconRes); + return this; + } +} diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java new file mode 100644 index 000000000..6f5a59923 --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java @@ -0,0 +1,35 @@ +package com.actionbarsherlock.internal.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +public class ActionBarContainer extends FrameLayout { + private boolean mIsTransitioning; + + public ActionBarContainer(Context context) { + this(context, null); + } + + public ActionBarContainer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return mIsTransitioning || super.onInterceptTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + return true; + } + + public void setTransitioning(boolean transitioning) { + mIsTransitioning = transitioning; + setDescendantFocusability(transitioning ? ViewGroup.FOCUS_BLOCK_DESCENDANTS : ViewGroup.FOCUS_AFTER_DESCENDANTS); + } +} diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarView.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarView.java new file mode 100644 index 000000000..2eb932ebe --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarView.java @@ -0,0 +1,696 @@ +package com.actionbarsherlock.internal.widget; + +import android.app.Activity; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.support.v4.app.ActionBar; +import android.support.v4.view.Window; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.Spinner; +import android.widget.SpinnerAdapter; +import android.widget.TextView; +import com.actionbarsherlock.R; +import com.actionbarsherlock.internal.view.menu.ActionMenuItem; +import com.actionbarsherlock.internal.view.menu.ActionMenuItemView; + +public final class ActionBarView extends RelativeLayout { + /** Default display options if none are defined in the theme. */ + private static final int DEFAULT_DISPLAY_OPTIONS = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME; + + /** Default navigation mode if one is not defined in the theme. */ + private static final int DEFAULT_NAVIGATION_MODE = ActionBar.NAVIGATION_MODE_STANDARD; + + + + private final View mHomeAsUpView; + private final ViewGroup mHomeLayout; + private final ActionMenuItem mLogoNavItem; + + private final CharSequence mTitle; + private final TextView mTitleLayout; + + private final CharSequence mSubtitle; + private final TextView mSubtitleLayout; + + /** Indeterminate progress bar. */ + private final ProgressBar mIndeterminateProgress; + + /** List view. */ + private final Spinner mSpinner; + private SpinnerAdapter mSpinnerAdapter; + private final AdapterView.OnItemSelectedListener mNavItemSelectedListener; + private ActionBar.OnNavigationListener mCallback; + + /** Custom view parent. */ + private final FrameLayout mCustomView; + private View mCustomNavView; + + private ImageView mIconView; + private Drawable mLogo; + private Drawable mIcon; + private final Drawable mDivider; + + /** Container for all action items. */ + private final LinearLayout mActionsView; + + /** Container for all tab items. */ + private final LinearLayout mTabsView; + + /** + * Display state flags. + * + * @see #getDisplayOptions() + * @see #getDisplayOptionValue(int) + * @see #setDisplayOptions(int) + * @see #setDisplayOptions(int, int) + * @see #setDisplayOption(int, boolean) + * @see #reloadDisplay() + */ + private int mDisplayOptions; + + /** + * Current navigation mode + * + * @see #getNavigationMode() + * @see #setNavigationMode(int) + */ + private int mNavigationMode = -1; + + private boolean mIsConstructing; + + + + public ActionBarView(Context context) { + this(context, null); + } + + public ActionBarView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ActionBarView(final Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mIsConstructing = true; + LayoutInflater.from(context).inflate(R.layout.abs__action_bar, this, true); + + mNavItemSelectedListener = new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView arg0, View arg1, int arg2, long arg3) { + if (mCallback != null) { + mCallback.onNavigationItemSelected(arg2, arg3); + } + } + + @Override + public void onNothingSelected(AdapterView arg0) { + //No op + } + }; + + setBackgroundResource(0); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SherlockTheme, defStyle, 0); + final ApplicationInfo appInfo = context.getApplicationInfo(); + final PackageManager pm = context.getPackageManager(); + + + //// TITLE //// + + mTitleLayout = (TextView)findViewById(R.id.abs__action_bar_title); + + //Try to load title style from the theme + final int titleTextStyle = a.getResourceId(R.styleable.SherlockTheme_abTitleTextStyle, 0); + if (titleTextStyle != 0) { + mTitleLayout.setTextAppearance(context, titleTextStyle); + } + + //Try to load title from the theme + mTitle = a.getString(R.styleable.SherlockTheme_abTitle); + if (mTitle != null) { + setTitle(mTitle); + } + + + //// SUBTITLE //// + + mSubtitleLayout = (TextView)findViewById(R.id.abs__action_bar_subtitle); + + //Try to load subtitle style from the theme + final int subtitleTextStyle = a.getResourceId(R.styleable.SherlockTheme_abSubtitleTextStyle, 0); + if (subtitleTextStyle != 0) { + mSubtitleLayout.setTextAppearance(context, subtitleTextStyle); + } + + //Try to load subtitle from theme + mSubtitle = a.getString(R.styleable.SherlockTheme_abSubtitle); + if (mSubtitle != null) { + setSubtitle(mSubtitle); + } + + + /// HOME //// + + mHomeLayout = (ViewGroup)findViewById(R.id.abs__home_wrapper); + final int homeLayoutResource = a.getResourceId(R.styleable.SherlockTheme_abHomeLayout, R.layout.abs__action_bar_home); + LayoutInflater.from(context).inflate(homeLayoutResource, mHomeLayout, true); + + //Try to load the logo from the theme + mLogo = a.getDrawable(R.styleable.SherlockTheme_abLogo); + /* + if ((mLogo == null) && (context instanceof Activity)) { + //LOGO LOADING DOES NOT WORK + //SEE: http://stackoverflow.com/questions/6105504/load-activity-and-or-application-logo-programmatically-from-manifest + //SEE: https://groups.google.com/forum/#!topic/android-developers/UFR4l0ZwJWc + } + */ + + //Try to load the icon from the theme + mIcon = a.getDrawable(R.styleable.SherlockTheme_abIcon); + if ((mIcon == null) && (context instanceof Activity)) { + mIcon = appInfo.loadIcon(pm); + } + + mHomeAsUpView = findViewById(R.id.abs__up); + mIconView = (ImageView)findViewById(R.id.abs__home); + + + //// NAVIGATION //// + + mSpinner = (Spinner)findViewById(R.id.abs__nav_list); + mSpinner.setOnItemSelectedListener(mNavItemSelectedListener); + + mTabsView = (LinearLayout)findViewById(R.id.abs__nav_tabs); + + + //// CUSTOM VIEW //// + + mCustomView = (FrameLayout)findViewById(R.id.abs__custom); + + //Try to load a custom view from the theme. This will NOT automatically + //trigger the visibility of the custom layout, however. + final int customViewResourceId = a.getResourceId(R.styleable.SherlockTheme_abCustomNavigationLayout, 0); + if (customViewResourceId != 0) { + mCustomNavView = LayoutInflater.from(context).inflate(customViewResourceId, mCustomView, true); + mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD; + setDisplayOptions(mDisplayOptions | ActionBar.DISPLAY_SHOW_CUSTOM); + } + + + + + mActionsView = (LinearLayout)findViewById(R.id.abs__actions); + mDivider = a.getDrawable(R.styleable.SherlockTheme_abDivider); + + mIndeterminateProgress = (ProgressBar)findViewById(R.id.abs__iprogress); + + //Try to get the display options defined in the theme, or fall back to + //displaying the title and home icon + setDisplayOptions(a.getInteger(R.styleable.SherlockTheme_abDisplayOptions, DEFAULT_DISPLAY_OPTIONS)); + + //Try to get the navigation defined in the theme, or, fall back to + //use standard navigation by default + setNavigationMode(a.getInteger(R.styleable.SherlockTheme_abNavigationMode, DEFAULT_NAVIGATION_MODE)); + + + //Reduce, Reuse, Recycle! + a.recycle(); + mIsConstructing = false; + + mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle); + mHomeLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (context instanceof Activity) { + ((Activity)context).onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem); + } + } + }); + mHomeLayout.setClickable(true); + mHomeLayout.setFocusable(true); + + reloadDisplay(); + } + + + + // ------------------------------------------------------------------------ + // HELPER METHODS + // ------------------------------------------------------------------------ + + /** + * Helper to get a boolean value for a specific flag. + * + * @param flag Target flag. + * @return Value. + */ + private boolean getDisplayOptionValue(int flag) { + return (mDisplayOptions & flag) == flag; + } + + /** + * Reload the current action bar display state. + */ + private void reloadDisplay() { + if (mIsConstructing) { + return; //Do not run if we are in the constructor + } + + final boolean isStandard = mNavigationMode == ActionBar.NAVIGATION_MODE_STANDARD; + final boolean isList = mNavigationMode == ActionBar.NAVIGATION_MODE_LIST; + final boolean isTab = mNavigationMode == ActionBar.NAVIGATION_MODE_TABS; + final boolean isTabUnderAb = isTab && getContext().getString(R.string.abs__tab_under_ab_tag).equals(mTabsView.getTag()); + final boolean hasSubtitle = (mSubtitleLayout.getText() != null) && !mSubtitleLayout.getText().equals(""); + final boolean displayHome = getDisplayOptionValue(ActionBar.DISPLAY_SHOW_HOME); + final boolean displayHomeAsUp = getDisplayOptionValue(ActionBar.DISPLAY_HOME_AS_UP); + final boolean displayTitle = getDisplayOptionValue(ActionBar.DISPLAY_SHOW_TITLE); + final boolean displayCustom = getDisplayOptionValue(ActionBar.DISPLAY_SHOW_CUSTOM); + final boolean displayLogo = getDisplayOptionValue(ActionBar.DISPLAY_USE_LOGO) && (mLogo != null); + + mHomeLayout.setVisibility(displayHome ? View.VISIBLE : View.GONE); + if (displayHome) { + if (mHomeAsUpView != null) { + mHomeAsUpView.setVisibility(displayHomeAsUp ? View.VISIBLE : View.GONE); + } + if (mIconView != null) { + mIconView.setImageDrawable(displayLogo ? mLogo : mIcon); + } + } + + //Only show list if we are in list navigation and there are list items + mSpinner.setVisibility(isList ? View.VISIBLE : View.GONE); + + // Show tabs if in tabs navigation mode. + mTabsView.setVisibility(isTab ? View.VISIBLE : View.GONE); + + //Show title view if we are not in list navigation, not showing custom + //view, and the show title flag is true + mTitleLayout.setVisibility((isStandard || isTabUnderAb) && !displayCustom && displayTitle ? View.VISIBLE : View.GONE); + //Show subtitle view if we are not in list navigation, not showing + //custom view, show title flag is true, and a subtitle is set + mSubtitleLayout.setVisibility((isStandard || isTabUnderAb) && !displayCustom && displayTitle && hasSubtitle ? View.VISIBLE : View.GONE); + //Show custom view if we are not in list navigation and showing custom + //flag is set + mCustomView.setVisibility(isStandard && displayCustom ? View.VISIBLE : View.GONE); + } + + // ------------------------------------------------------------------------ + // ACTION BAR API + // ------------------------------------------------------------------------ + + public void addTab(ActionBar.Tab tab) { + final int tabCount = getTabCount(); + addTab(tab, tabCount, tabCount == 0); + } + + public void addTab(ActionBar.Tab tab, boolean setSelected) { + addTab(tab, getTabCount(), setSelected); + } + + public void addTab(ActionBar.Tab tab, int position) { + addTab(tab, position, getTabCount() == 0); + } + + public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { + mTabsView.addView(((TabImpl)tab).mView, position); + if (setSelected) { + tab.select(); + } + } + + public View getCustomView() { + return mCustomNavView; + } + + public int getDisplayOptions() { + return mDisplayOptions; + } + + public SpinnerAdapter getDropdownAdapter() { + return this.mSpinnerAdapter; + } + + public int getDropdownSelectedPosition() { + return this.mSpinner.getSelectedItemPosition(); + } + + public int getNavigationMode() { + return mNavigationMode; + } + + public ActionBar.Tab getSelectedTab() { + final int count = mTabsView.getChildCount(); + for (int i = 0; i < count; i++) { + TabImpl tab = (TabImpl)mTabsView.getChildAt(i).getTag(); + if (tab.mView.isSelected()) { + return tab; + } + } + return null; + } + + public CharSequence getSubtitle() { + if ((mNavigationMode == ActionBar.NAVIGATION_MODE_STANDARD) && !mSubtitleLayout.getText().equals("")) { + return mSubtitleLayout.getText(); + } else { + return null; + } + } + + public ActionBar.Tab getTabAt(int index) { + View view = mTabsView.getChildAt(index); + return (view != null) ? (TabImpl)view.getTag() : null; + } + + public int getTabCount() { + return mTabsView.getChildCount(); + } + + public CharSequence getTitle() { + if ((mNavigationMode == ActionBar.NAVIGATION_MODE_STANDARD) && !mTitleLayout.getText().equals("")) { + return mTitleLayout.getText(); + } else { + return null; + } + } + + public TabImpl newTab() { + return new TabImpl(this); + } + + public void removeAllTabs() { + TabImpl selected = (TabImpl)getSelectedTab(); + if (selected != null) { + selected.unselect(); + } + mTabsView.removeAllViews(); + } + + public void removeTab(ActionBar.Tab tab) { + final int count = mTabsView.getChildCount(); + for (int i = 0; i < count; i++) { + TabImpl existingTab = (TabImpl)mTabsView.getChildAt(i).getTag(); + if (existingTab.equals(tab)) { + removeTabAt(i); + break; + } + } + } + + public void removeTabAt(int position) { + TabImpl tab = (TabImpl)getTabAt(position); + if (tab != null) { + tab.unselect(); + mTabsView.removeViewAt(position); + + if (position > 0) { + //Select previous tab + ((TabImpl)mTabsView.getChildAt(position - 1).getTag()).select(); + } else if (mTabsView.getChildCount() > 0) { + //Select first tab + ((TabImpl)mTabsView.getChildAt(0).getTag()).select(); + } + } + } + + public void setCallback(ActionBar.OnNavigationListener callback) { + mCallback = callback; + } + + public void setCustomNavigationView(View view) { + mCustomNavView = view; + mCustomView.removeAllViews(); + mCustomView.addView(view); + } + + public void setDisplayOptions(int options) { + mDisplayOptions = options; + reloadDisplay(); + } + + public void setDropdownAdapter(SpinnerAdapter spinnerAdapter) { + mSpinnerAdapter = spinnerAdapter; + if (mSpinner != null) { + mSpinner.setAdapter(mSpinnerAdapter); + } + } + + public void setDropdownSelectedPosition(int position) { + mSpinner.setSelection(position); + } + + public void setProgressBarIndeterminateVisibility(boolean visible) { + mIndeterminateProgress.setVisibility(visible ? View.VISIBLE : View.GONE); + } + + public void setNavigationMode(int mode) { + if ((mode != ActionBar.NAVIGATION_MODE_STANDARD) && (mode != ActionBar.NAVIGATION_MODE_LIST) + && (mode != ActionBar.NAVIGATION_MODE_TABS)) { + throw new IllegalArgumentException("Unknown navigation mode value " + Integer.toString(mode)); + } + + if (mode != mNavigationMode) { + mNavigationMode = mode; + reloadDisplay(); + } + } + + public void selectTab(ActionBar.Tab tab) { + final int count = mTabsView.getChildCount(); + for (int i = 0; i < count; i++) { + TabImpl existingTab = (TabImpl)mTabsView.getChildAt(i).getTag(); + if (existingTab.equals(tab)) { + existingTab.select(); + break; + } + } + } + + public void setSubtitle(CharSequence subtitle) { + mSubtitleLayout.setText((subtitle == null) ? "" : subtitle); + reloadDisplay(); + } + + public void setSubtitle(int resId) { + mSubtitleLayout.setText(resId); + reloadDisplay(); + } + + public void setTitle(CharSequence title) { + mTitleLayout.setText((title == null) ? "" : title); + } + + public void setTitle(int resId) { + mTitleLayout.setText(resId); + } + + // ------------------------------------------------------------------------ + // ACTION ITEMS SUPPORT + // ------------------------------------------------------------------------ + + public ActionMenuItemView newItem() { + ActionMenuItemView item = (ActionMenuItemView)LayoutInflater.from(getContext()).inflate(R.layout.abs__action_bar_item_layout, mActionsView, false); + return item; + } + + public void addItem(ActionMenuItemView item) { + if (mDivider != null) { + ImageView divider = new ImageView(getContext()); + divider.setImageDrawable(mDivider); + divider.setScaleType(ImageView.ScaleType.FIT_XY); + + LinearLayout.LayoutParams dividerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.FILL_PARENT + ); + + mActionsView.addView(divider, dividerParams); + item.setDivider(divider); + } + + mActionsView.addView(item); + } + + public void removeAllItems() { + mActionsView.removeAllViews(); + } + + // ------------------------------------------------------------------------ + // HELPER INTERFACES AND HELPER CLASSES + // ------------------------------------------------------------------------ + + private static class TabImpl extends ActionBar.Tab { + private static final View.OnClickListener clickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + ((TabImpl)v.getTag()).select(); + } + }; + + final ActionBarView mActionBar; + final View mView; + final ImageView mIconView; + final TextView mTextView; + final FrameLayout mCustomView; + + ActionBar.TabListener mListener; + Object mTag; + + + TabImpl(ActionBarView actionBar) { + mActionBar = actionBar; + mView = LayoutInflater.from(mActionBar.getContext()).inflate(R.layout.abs__action_bar_tab_layout, actionBar.mTabsView, false); + mView.setTag(this); + mView.setOnClickListener(clickListener); + + mIconView = (ImageView)mView.findViewById(R.id.abs__tab_icon); + mTextView = (TextView)mView.findViewById(R.id.abs__tab); + mCustomView = (FrameLayout)mView.findViewById(R.id.abs__tab_custom); + } + + /** + * Update display to reflect current property state. + */ + void reloadDisplay() { + boolean hasCustom = mCustomView.getChildCount() > 0; + mIconView.setVisibility(hasCustom ? View.GONE : View.VISIBLE); + mTextView.setVisibility(hasCustom ? View.GONE : View.VISIBLE); + mCustomView.setVisibility(hasCustom ? View.VISIBLE : View.GONE); + } + + @Override + public View getCustomView() { + return mCustomView.getChildAt(0); + } + + @Override + public Drawable getIcon() { + return mIconView.getDrawable(); + } + + @Override + public int getPosition() { + final int count = mActionBar.mTabsView.getChildCount(); + for (int i = 0; i < count; i++) { + if (mActionBar.mTabsView.getChildAt(i).getTag().equals(this)) { + return i; + } + } + return -1; + } + + @Override + public ActionBar.TabListener getTabListener() { + return mListener; + } + + @Override + public Object getTag() { + return mTag; + } + + @Override + public CharSequence getText() { + return mTextView.getText(); + } + + @Override + public TabImpl setCustomView(int layoutResId) { + mCustomView.removeAllViews(); + LayoutInflater.from(mActionBar.getContext()).inflate(layoutResId, mCustomView, true); + reloadDisplay(); + return this; + } + + @Override + public TabImpl setCustomView(View view) { + mCustomView.removeAllViews(); + if (view != null) { + mCustomView.addView(view); + } + reloadDisplay(); + return this; + } + + @Override + public TabImpl setIcon(Drawable icon) { + mIconView.setImageDrawable(icon); + return this; + } + + @Override + public TabImpl setIcon(int resId) { + mIconView.setImageResource(resId); + return this; + } + + @Override + public TabImpl setTabListener(ActionBar.TabListener listener) { + mListener = listener; + return this; + } + + @Override + public TabImpl setTag(Object obj) { + mTag = obj; + return this; + } + + @Override + public TabImpl setText(int resId) { + mTextView.setText(resId); + return this; + } + + @Override + public TabImpl setText(CharSequence text) { + mTextView.setText(text); + return this; + } + + @Override + public void select() { + if (mView.isSelected()) { + if (mListener != null) { + mListener.onTabReselected(this, null); + } + return; + } + + TabImpl current = (TabImpl)mActionBar.getSelectedTab(); + if (current != null) { + current.unselect(); + } + + mView.setSelected(true); + if (mListener != null) { + mListener.onTabSelected(this, null); + } + } + + /** + * Unselect this tab. Only valid if the tab has been added to the + * action bar and was previously selected. + */ + void unselect() { + if (mView.isSelected()) { + mView.setSelected(false); + + if (mListener != null) { + mListener.onTabUnselected(this, null); + } + } + } + } +} diff --git a/actionbarsherlock/library/src/com/actionbarsherlock/internal/widget/ScrollingTextView.java b/actionbarsherlock/library/src/com/actionbarsherlock/internal/widget/ScrollingTextView.java new file mode 100644 index 000000000..edd5ec0a4 --- /dev/null +++ b/actionbarsherlock/library/src/com/actionbarsherlock/internal/widget/ScrollingTextView.java @@ -0,0 +1,51 @@ +package com.actionbarsherlock.internal.widget; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.widget.TextView; + +/** + * @author marco + * Workaround to be able to scroll text inside a TextView without it required + * to be focused. For some strange reason there isn't an easy way to do this + * natively. + * + * Original code written by Evan Cummings: + * http://androidbears.stellarpc.net/?p=185 + */ +public final class ScrollingTextView extends TextView { + + public ScrollingTextView(Context context, AttributeSet attrs, + int defStyle) { + super(context, attrs, defStyle); + } + + public ScrollingTextView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ScrollingTextView(Context context) { + super(context); + } + + @Override + protected void onFocusChanged(boolean focused, int direction, + Rect previouslyFocusedRect) { + if (focused) { + super.onFocusChanged(focused, direction, previouslyFocusedRect); + } + } + + @Override + public void onWindowFocusChanged(boolean focused) { + if (focused) { + super.onWindowFocusChanged(focused); + } + } + + @Override + public boolean isFocused() { + return true; + } +} diff --git a/actionbarsherlock/plugins/README.md b/actionbarsherlock/plugins/README.md new file mode 100644 index 000000000..6c241e520 --- /dev/null +++ b/actionbarsherlock/plugins/README.md @@ -0,0 +1,8 @@ +ActionBarSherlock Plugins +========================= + +Modules provided in this folder are plugins which add functionality to the +ActionBarSherlock library. + +The requirements for implementing each pluging are detailed in the `README.md` +file in each plugin folder. diff --git a/actionbarsherlock/plugins/maps/AndroidManifest.xml b/actionbarsherlock/plugins/maps/AndroidManifest.xml new file mode 100644 index 000000000..8615e26fa --- /dev/null +++ b/actionbarsherlock/plugins/maps/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/actionbarsherlock/plugins/maps/README.md b/actionbarsherlock/plugins/maps/README.md new file mode 100644 index 000000000..a45dd1ffb --- /dev/null +++ b/actionbarsherlock/plugins/maps/README.md @@ -0,0 +1,18 @@ +ActionBarSherlock Plugin: Maps +============================== + +This plugin provides support for the Google API `MapView` widget through an +additional base activity, `FragmentMapActivity`. Your application must adhere +to all of the regular requirements of implementing the [Google Map View][1]. + +By using `FragmentMapActivity` you are afforded the use of fragments, loaders, +and the action bar--including support for using a `MapView` within a fragment. +You can find examples of these implementations in the 'demos' sample. + + + + + + + + [1]: http://developer.android.com/resources/tutorials/views/hello-mapview.html diff --git a/actionbarsherlock/plugins/maps/checkstyle.xml b/actionbarsherlock/plugins/maps/checkstyle.xml new file mode 100644 index 000000000..91bcb9a52 --- /dev/null +++ b/actionbarsherlock/plugins/maps/checkstyle.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/plugins/maps/pom.xml b/actionbarsherlock/plugins/maps/pom.xml new file mode 100644 index 000000000..4699b215f --- /dev/null +++ b/actionbarsherlock/plugins/maps/pom.xml @@ -0,0 +1,75 @@ + + + + 4.0.0 + + com.actionbarsherlock + plugin-maps + Plugin: Maps + jar + + + com.actionbarsherlock + parent-plugins + 3.4.2 + ../pom.xml + + + + + android + android + provided + + + + com.google.android.maps + maps + provided + + + + com.actionbarsherlock + library + ${project.version} + provided + + + + + ${apk.prefix}-${project.artifactId}-${project.version} + src + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + ${project.basedir}/checkstyle.xml + + + + verify + + checkstyle + + + + + + + diff --git a/actionbarsherlock/plugins/maps/project.properties b/actionbarsherlock/plugins/maps/project.properties new file mode 100644 index 000000000..e5171db54 --- /dev/null +++ b/actionbarsherlock/plugins/maps/project.properties @@ -0,0 +1,13 @@ +# 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=Google Inc.:Google APIs:13 +android.library.reference.1=../../library diff --git a/actionbarsherlock/plugins/maps/src/android/support/v4/app/FragmentMapActivity.java b/actionbarsherlock/plugins/maps/src/android/support/v4/app/FragmentMapActivity.java new file mode 100644 index 000000000..44604ad1d --- /dev/null +++ b/actionbarsherlock/plugins/maps/src/android/support/v4/app/FragmentMapActivity.java @@ -0,0 +1,1240 @@ +/* + * Copyright (C) 2011 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 android.support.v4.app; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import com.actionbarsherlock.R; +import com.actionbarsherlock.internal.app.ActionBarWrapper; +import com.actionbarsherlock.internal.app.ActionBarImpl; +import com.actionbarsherlock.internal.view.menu.MenuBuilder; +import com.actionbarsherlock.internal.view.menu.MenuInflaterWrapper; +import com.actionbarsherlock.internal.view.menu.MenuItemImpl; +import com.actionbarsherlock.internal.view.menu.MenuItemWrapper; +import com.actionbarsherlock.internal.view.menu.MenuWrapper; +import com.google.android.maps.MapActivity; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.content.res.Resources.Theme; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Parcelable; +import android.support.v4.view.ActionMode; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuInflater; +import android.support.v4.view.MenuItem; +import android.support.v4.view.Window; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.widget.FrameLayout; + +/** + * Base class for activities that want to use the support-based ActionBar, + * Fragment, Loader, and Google Map APIs. + * + *

Known limitations:

+ *
    + *
  • When using the <fragment> tag, this implementation can not + * use the parent view's ID as the new fragment's ID. You must explicitly + * specify an ID (or tag) in the <fragment>.

    + *
  • Prior to Honeycomb (3.0), an activity's state was saved before pausing. + * Fragments are a significant amount of new state, and dynamic enough that one + * often wants them to change between pausing and stopping. These classes + * throw an exception if you try to change the fragment state after it has been + * saved, to avoid accidental loss of UI state. However this is too restrictive + * prior to Honeycomb, where the state is saved before pausing. To address this, + * when running on platforms prior to Honeycomb an exception will not be thrown + * if you change fragments between the state save and the activity being stopped. + * This means that is some cases if the activity is restored from its last saved + * state, this may be a snapshot slightly before what the user last saw.

    + *
+ */ +public abstract class FragmentMapActivity extends MapActivity implements SupportActivity { + private static final String TAG = "FragmentMapActivity"; + private static final boolean DEBUG = false; + + private static final String FRAGMENTS_TAG = "android:support:fragments"; + + static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; + + static final int MSG_REALLY_STOPPED = 1; + static final int MSG_RESUME_PENDING = 2; + + private static final int WINDOW_FLAG_ACTION_BAR = 1 << Window.FEATURE_ACTION_BAR; + private static final int WINDOW_FLAG_ACTION_BAR_ITEM_TEXT = 1 << Window.FEATURE_ACTION_BAR_ITEM_TEXT; + private static final int WINDOW_FLAG_ACTION_BAR_OVERLAY = 1 << Window.FEATURE_ACTION_BAR_OVERLAY; + private static final int WINDOW_FLAG_ACTION_MODE_OVERLAY = 1 << Window.FEATURE_ACTION_MODE_OVERLAY; + private static final int WINDOW_FLAG_INDETERMINANTE_PROGRESS = 1 << Window.FEATURE_INDETERMINATE_PROGRESS; + + final SupportActivity.InternalCallbacks mInternalCallbacks = new SupportActivity.InternalCallbacks() { + @Override + void invalidateSupportFragmentIndex(int index) { + FragmentMapActivity.this.invalidateSupportFragmentIndex(index); + } + + @Override + LoaderManagerImpl getLoaderManager(int index, boolean started, boolean create) { + return FragmentMapActivity.this.getLoaderManager(index, started, create); + } + + @Override + Handler getHandler() { + return mHandler; + } + + @Override + FragmentManagerImpl getFragments() { + return mFragments; + } + + @Override + void ensureSupportActionBarAttached() { + FragmentMapActivity.this.ensureSupportActionBarAttached(); + } + + @Override + boolean getRetaining() { + return mRetaining; + } + }; + + final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_REALLY_STOPPED: + if (mStopped) { + doReallyStop(false); + } + break; + case MSG_RESUME_PENDING: + mFragments.dispatchResume(); + mFragments.execPendingActions(); + break; + default: + super.handleMessage(msg); + } + } + }; + final FragmentManagerImpl mFragments = new FragmentManagerImpl(); + + ActionBar mActionBar; + boolean mIsActionBarImplAttached; + long mWindowFlags = 0; + + final MenuBuilder mSupportMenu; + final MenuBuilder.Callback mSupportMenuCallback = new MenuBuilder.Callback() { + @Override + public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { + return FragmentMapActivity.this.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item); + } + }; + + boolean mCreated; + boolean mResumed; + boolean mStopped; + boolean mReallyStopped; + boolean mRetaining; + + boolean mOptionsMenuInvalidated; + boolean mOptionsMenuCreateResult; + + boolean mCheckedForLoaderManager; + boolean mLoadersStarted; + HCSparseArray mAllLoaderManagers; + LoaderManagerImpl mLoaderManager; + + static final class NonConfigurationInstances { + Object activity; + Object custom; + HashMap children; + ArrayList fragments; + HCSparseArray loaders; + } + + static class FragmentTag { + public static final int[] Fragment = { + 0x01010003, 0x010100d0, 0x010100d1 + }; + public static final int Fragment_id = 1; + public static final int Fragment_name = 0; + public static final int Fragment_tag = 2; + } + + + + public FragmentMapActivity() { + super(); + + if (IS_HONEYCOMB) { + mActionBar = ActionBarWrapper.createFor(this); + mSupportMenu = null; //Everything should be done natively + } else { + mSupportMenu = new MenuBuilder(this); + mSupportMenu.setCallback(mSupportMenuCallback); + } + } + + @Override + public SupportActivity.InternalCallbacks getInternalCallbacks() { + return mInternalCallbacks; + } + + @Override + public Activity asActivity() { + return this; + } + + protected void ensureSupportActionBarAttached() { + if (IS_HONEYCOMB || mIsActionBarImplAttached) { + return; + } + if (!isChild()) { + if ((mWindowFlags & WINDOW_FLAG_ACTION_BAR) == WINDOW_FLAG_ACTION_BAR) { + if ((mWindowFlags & WINDOW_FLAG_ACTION_BAR_OVERLAY) == WINDOW_FLAG_ACTION_BAR_OVERLAY) { + super.setContentView(R.layout.abs__screen_action_bar_overlay); + } else { + super.setContentView(R.layout.abs__screen_action_bar); + } + + mActionBar = new ActionBarImpl(this); + ((ActionBarImpl)mActionBar).init(); + + final boolean textEnabled = ((mWindowFlags & WINDOW_FLAG_ACTION_BAR_ITEM_TEXT) == WINDOW_FLAG_ACTION_BAR_ITEM_TEXT); + mSupportMenu.setShowsActionItemText(textEnabled); + + if ((mWindowFlags & WINDOW_FLAG_INDETERMINANTE_PROGRESS) == WINDOW_FLAG_INDETERMINANTE_PROGRESS) { + ((ActionBarImpl)mActionBar).setProgressBarIndeterminateVisibility(false); + } + + //TODO set other flags + } else { + if ((mWindowFlags & WINDOW_FLAG_INDETERMINANTE_PROGRESS) == WINDOW_FLAG_INDETERMINANTE_PROGRESS) { + super.requestWindowFeature((int)Window.FEATURE_INDETERMINATE_PROGRESS); + } + super.setContentView(R.layout.abs__screen_simple); + } + } + + invalidateOptionsMenu(); + mIsActionBarImplAttached = true; + } + + // ------------------------------------------------------------------------ + // HOOKS INTO ACTIVITY + // ------------------------------------------------------------------------ + + /** + * Enable extended window features. + * + * @param featureId The desired feature as defined in + * {@link android.support.v4.view.Window}. + * @return Returns {@code true} if the requested feature is supported and + * now enabled. + */ + @Override + public boolean requestWindowFeature(long featureId) { + if (!IS_HONEYCOMB) { + switch ((int)featureId) { + case (int)Window.FEATURE_ACTION_BAR: + case (int)Window.FEATURE_ACTION_BAR_ITEM_TEXT: + case (int)Window.FEATURE_ACTION_BAR_OVERLAY: + case (int)Window.FEATURE_ACTION_MODE_OVERLAY: + case (int)Window.FEATURE_INDETERMINATE_PROGRESS: + mWindowFlags |= (1 << featureId); + return true; + } + } + return super.requestWindowFeature((int)featureId); + } + + @Override + public android.view.MenuInflater getMenuInflater() { + if (IS_HONEYCOMB) { + if (DEBUG) Log.d(TAG, "getMenuInflater(): Wrapping native inflater."); + + //Wrap the native inflater so it can unwrap the native menu first + return new MenuInflaterWrapper(this, super.getMenuInflater()); + } + + if (DEBUG) Log.d(TAG, "getMenuInflater(): Returning support inflater."); + + //Use our custom menu inflater + return new MenuInflater(this, super.getMenuInflater()); + } + + @Override + public void setContentView(int layoutResId) { + ensureSupportActionBarAttached(); + if (IS_HONEYCOMB || isChild()) { + super.setContentView(layoutResId); + } else { + FrameLayout contentView = (FrameLayout)findViewById(R.id.abs__content); + contentView.removeAllViews(); + getLayoutInflater().inflate(layoutResId, contentView, true); + } + } + + @Override + public void setContentView(View view, LayoutParams params) { + ensureSupportActionBarAttached(); + if (IS_HONEYCOMB || isChild()) { + super.setContentView(view, params); + } else { + FrameLayout contentView = (FrameLayout)findViewById(R.id.abs__content); + contentView.removeAllViews(); + contentView.addView(view, params); + } + } + + @Override + public void setContentView(View view) { + ensureSupportActionBarAttached(); + if (IS_HONEYCOMB || isChild()) { + super.setContentView(view); + } else { + FrameLayout contentView = (FrameLayout)findViewById(R.id.abs__content); + contentView.removeAllViews(); + contentView.addView(view); + } + } + + @Override + public void setTitle(CharSequence title) { + if (IS_HONEYCOMB || (getSupportActionBar() == null)) { + super.setTitle(title); + } else { + getSupportActionBar().setTitle(title); + } + } + + @Override + public void setTitle(int titleId) { + if (IS_HONEYCOMB || (getSupportActionBar() == null)) { + super.setTitle(titleId); + } else { + getSupportActionBar().setTitle(titleId); + } + } + + /** + * Dispatch incoming result to the correct fragment. + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + int index = requestCode>>16; + if (index != 0) { + index--; + if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) { + Log.w(TAG, "Activity result fragment index out of range: 0x" + + Integer.toHexString(requestCode)); + return; + } + Fragment frag = mFragments.mActive.get(index); + if (frag == null) { + Log.w(TAG, "Activity result no fragment exists for index: 0x" + + Integer.toHexString(requestCode)); + } else { + frag.onActivityResult(requestCode&0xffff, resultCode, data); + } + return; + } + + super.onActivityResult(requestCode, resultCode, data); + } + + @Override + protected void onApplyThemeResource(Theme theme, int resid, boolean first) { + TypedArray attrs = theme.obtainStyledAttributes(resid, R.styleable.SherlockTheme); + + final boolean actionBar = attrs.getBoolean(R.styleable.SherlockTheme_windowActionBar, false); + mWindowFlags |= actionBar ? WINDOW_FLAG_ACTION_BAR : 0; + + final boolean actionModeOverlay = attrs.getBoolean(R.styleable.SherlockTheme_windowActionModeOverlay, false); + mWindowFlags |= actionModeOverlay ? WINDOW_FLAG_ACTION_MODE_OVERLAY : 0; + + attrs.recycle(); + super.onApplyThemeResource(theme, resid, first); + } + + /** + * Take care of popping the fragment back stack or finishing the activity + * as appropriate. + */ + @Override + public void onBackPressed() { + if (!mFragments.popBackStackImmediate()) { + finish(); + } + } + + /** + * Dispatch configuration change to all fragments. + */ + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mFragments.dispatchConfigurationChanged(newConfig); + } + + /** + * Perform initialization of all fragments and loaders. + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + mFragments.attachActivity(this); + // Old versions of the platform didn't do this! + if (getLayoutInflater().getFactory() == null) { + getLayoutInflater().setFactory(this); + } + + super.onCreate(savedInstanceState); + + NonConfigurationInstances nc = (NonConfigurationInstances) + getLastNonConfigurationInstance(); + if (nc != null) { + mAllLoaderManagers = nc.loaders; + } + if (savedInstanceState != null) { + Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); + mFragments.restoreAllState(p, nc != null ? nc.fragments : null); + } + mFragments.dispatchCreate(); + } + + /** + *

Initialize the contents of the Activity's standard options menu. You + * should place your menu items in to menu.

+ * + *

The default implementation populates the menu with standard system + * menu items. These are placed in the {@link Menu.CATEGORY_SYSTEM} group + * so that they will be correctly ordered with application-defined menu + * items. Deriving classes should always call through to the base + * implementation.

+ * + *

You can safely hold on to menu (and any items created from it), + * making modifications to it as desired, until the next time + * {@code onCreateOptionsMenu()} is called.

+ * + *

When you add items to the menu, you can implement the Activity's + * {@link #onOptionsItemSelected(MenuItem)} method to handle them + * there.

+ * + * @param menu The options menu in which you place your items. + * @return You must return true for the menu to be displayed; if you return + * false it will not be shown. + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu(Menu): Returning " + menu.hasVisibleItems()); + return menu.hasVisibleItems(); + } + + @Override + public final boolean onCreateOptionsMenu(android.view.Menu menu) { + // Prior to Honeycomb, the framework can't invalidate the options + // menu, so we must always say we have one in case the app later + // invalidates it and needs to have it shown. + boolean result = true; + + if (IS_HONEYCOMB) { + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu(android.view.Menu): Calling support method with wrapped native menu."); + MenuWrapper wrapped = new MenuWrapper(menu); + result = onCreateOptionsMenu(wrapped); + result |= mFragments.dispatchCreateOptionsMenu(wrapped, getMenuInflater()); + } + + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu(android.view.Menu): Returning " + result); + return result; + } + + /** + * Add support for inflating the <fragment> tag. + */ + @Override + public View onCreateView(String name, Context context, AttributeSet attrs) { + if (!"fragment".equals(name)) { + return super.onCreateView(name, context, attrs); + } + + String fname = attrs.getAttributeValue(null, "class"); + TypedArray a = context.obtainStyledAttributes(attrs, FragmentTag.Fragment); + if (fname == null) { + fname = a.getString(FragmentTag.Fragment_name); + } + int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID); + String tag = a.getString(FragmentTag.Fragment_tag); + a.recycle(); + + View parent = null; // NOTE: no way to get parent pre-Honeycomb. + int containerId = parent != null ? parent.getId() : 0; + if (containerId == View.NO_ID && id == View.NO_ID && tag == null) { + throw new IllegalArgumentException(attrs.getPositionDescription() + + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname); + } + + // If we restored from a previous state, we may already have + // instantiated this fragment from the state and should use + // that instance instead of making a new one. + Fragment fragment = id != View.NO_ID ? mFragments.findFragmentById(id) : null; + if (fragment == null && tag != null) { + fragment = mFragments.findFragmentByTag(tag); + } + if (fragment == null && containerId != View.NO_ID) { + fragment = mFragments.findFragmentById(containerId); + } + + if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x" + + Integer.toHexString(id) + " fname=" + fname + + " existing=" + fragment); + if (fragment == null) { + fragment = Fragment.instantiate(this, fname); + fragment.mFromLayout = true; + fragment.mFragmentId = id != 0 ? id : containerId; + fragment.mContainerId = containerId; + fragment.mTag = tag; + fragment.mInLayout = true; + fragment.mFragmentManager = mFragments; + fragment.onInflate(this, attrs, fragment.mSavedFragmentState); + mFragments.addFragment(fragment, true); + + } else if (fragment.mInLayout) { + // A fragment already exists and it is not one we restored from + // previous state. + throw new IllegalArgumentException(attrs.getPositionDescription() + + ": Duplicate id 0x" + Integer.toHexString(id) + + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId) + + " with another fragment for " + fname); + } else { + // This fragment was retained from a previous instance; get it + // going now. + fragment.mInLayout = true; + // If this fragment is newly instantiated (either right now, or + // from last saved state), then give it the attributes to + // initialize itself. + if (!fragment.mRetaining) { + fragment.onInflate(this, attrs, fragment.mSavedFragmentState); + } + mFragments.moveToState(fragment); + } + + if (fragment.mView == null) { + throw new IllegalStateException("Fragment " + fname + + " did not create a view."); + } + if (id != 0) { + fragment.mView.setId(id); + } + if (fragment.mView.getTag() == null) { + fragment.mView.setTag(tag); + } + return fragment.mView; + } + + @Override + public void invalidateOptionsMenu() { + if (DEBUG) Log.d(TAG, "supportInvalidateOptionsMenu(): Invalidating menu."); + + if (IS_HONEYCOMB) { + HoneycombInvalidateOptionsMenu.invoke(this); + } else { + mSupportMenu.clear(); + + mOptionsMenuCreateResult = onCreateOptionsMenu(mSupportMenu); + mOptionsMenuCreateResult |= mFragments.dispatchCreateOptionsMenu(mSupportMenu, getMenuInflater()); + + if (getSupportActionBar() != null) { + if (onPrepareOptionsMenu(mSupportMenu)) { + mFragments.dispatchPrepareOptionsMenu(mSupportMenu); + } + + //Since we now know we are using a custom action bar, perform the + //inflation callback to allow it to display any items it wants. + ((ActionBarImpl)mActionBar).onMenuInflated(mSupportMenu); + } + + // Whoops, older platform... we'll use a hack, to manually rebuild + // the options menu the next time it is prepared. + mOptionsMenuInvalidated = true; + } + } + + private static final class HoneycombInvalidateOptionsMenu { + static void invoke(Activity activity) { + activity.getWindow().invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); + } + } + + /** + * Destroy all fragments and loaders. + */ + @Override + protected void onDestroy() { + super.onDestroy(); + + doReallyStop(false); + + mFragments.dispatchDestroy(); + if (mLoaderManager != null) { + mLoaderManager.doDestroy(); + } + } + + /** + * Take care of calling onBackPressed() for pre-Eclair platforms. + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (android.os.Build.VERSION.SDK_INT < 5 /* ECLAIR */ + && keyCode == KeyEvent.KEYCODE_BACK + && event.getRepeatCount() == 0) { + // Take care of calling this method on earlier versions of + // the platform where it doesn't exist. + onBackPressed(); + return true; + } + + return super.onKeyDown(keyCode, event); + } + + /** + * Dispatch onLowMemory() to all fragments. + */ + @Override + public void onLowMemory() { + super.onLowMemory(); + mFragments.dispatchLowMemory(); + } + + /** + * Dispatch context and options menu to fragments. + */ + @Override + public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) { + if (super.onMenuItemSelected(featureId, item)) { + return true; + } + + switch (featureId) { + case Window.FEATURE_OPTIONS_PANEL: + return mFragments.dispatchOptionsItemSelected(new MenuItemWrapper(item)); + + case Window.FEATURE_CONTEXT_MENU: + return mFragments.dispatchContextItemSelected(new MenuItemWrapper(item)); + + default: + return false; + } + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + if (onOptionsItemSelected(item)) { + return true; + } + + switch (featureId) { + case Window.FEATURE_OPTIONS_PANEL: + return mFragments.dispatchOptionsItemSelected(item); + + case Window.FEATURE_CONTEXT_MENU: + return mFragments.dispatchContextItemSelected(item); + + default: + return false; + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return super.onOptionsItemSelected(item); + } + + @Override + public final boolean onOptionsItemSelected(android.view.MenuItem item) { + return onOptionsItemSelected(new MenuItemWrapper(item)); + } + + /** + * Call onOptionsMenuClosed() on fragments. + */ + @Override + public void onPanelClosed(int featureId, android.view.Menu menu) { + switch (featureId) { + case Window.FEATURE_OPTIONS_PANEL: + mFragments.dispatchOptionsMenuClosed(new MenuWrapper(menu)); + + if (!IS_HONEYCOMB && (getSupportActionBar() != null)) { + if (DEBUG) Log.d(TAG, "onPanelClosed(int, android.view.Menu): Dispatch menu visibility false to custom action bar."); + ((ActionBarImpl)mActionBar).onMenuVisibilityChanged(false); + } + break; + } + super.onPanelClosed(featureId, menu); + } + + /** + * Dispatch onPause() to fragments. + */ + @Override + protected void onPause() { + super.onPause(); + mResumed = false; + if (mHandler.hasMessages(MSG_RESUME_PENDING)) { + mHandler.removeMessages(MSG_RESUME_PENDING); + mFragments.dispatchResume(); + } + mFragments.dispatchPause(); + } + + /** + * Dispatch onResume() to fragments. + */ + @Override + protected void onResume() { + super.onResume(); + mHandler.sendEmptyMessage(MSG_RESUME_PENDING); + mResumed = true; + mFragments.execPendingActions(); + } + + /** + * Dispatch onResume() to fragments. + */ + @Override + protected void onPostResume() { + super.onPostResume(); + mHandler.removeMessages(MSG_RESUME_PENDING); + mFragments.dispatchResume(); + mFragments.execPendingActions(); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + boolean result = menu.hasVisibleItems(); + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(Menu): Returning " + result); + return result; + } + + @Override + public final boolean onPrepareOptionsMenu(android.view.Menu menu) { + boolean result = super.onPrepareOptionsMenu(menu); + + if (!IS_HONEYCOMB) { + if (DEBUG) { + Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): mOptionsMenuCreateResult = " + mOptionsMenuCreateResult); + Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): mOptionsMenuInvalidated = " + mOptionsMenuInvalidated); + } + + boolean prepareResult = true; + if (mOptionsMenuCreateResult) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Calling support method with custom menu."); + prepareResult = onPrepareOptionsMenu(mSupportMenu); + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Support method result returned " + prepareResult); + if (prepareResult) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Dispatching fragment method with custom menu."); + mFragments.dispatchPrepareOptionsMenu(mSupportMenu); + } + } + + if (mOptionsMenuInvalidated) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Clearing existing options menu."); + menu.clear(); + mOptionsMenuInvalidated = false; + + if (mOptionsMenuCreateResult && prepareResult) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Adding any action items that are not displayed on the action bar."); + //Only add items that have not already been added to our custom + //action bar implementation + for (MenuItemImpl item : mSupportMenu.getItems()) { + if (!item.isShownOnActionBar()) { + item.addTo(menu); + } + } + } + } + + if (mOptionsMenuCreateResult && prepareResult && menu.hasVisibleItems()) { + if (getSupportActionBar() != null) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Dispatch menu visibility true to custom action bar."); + ((ActionBarImpl)mActionBar).onMenuVisibilityChanged(true); + } + result = true; + } + } else { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Calling support method with wrapped native menu."); + final MenuWrapper wrappedMenu = new MenuWrapper(menu); + result = onPrepareOptionsMenu(wrappedMenu); + if (result) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Dispatching fragment method with wrapped native menu."); + mFragments.dispatchPrepareOptionsMenu(wrappedMenu); + } + } + + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Returning " + result); + return result; + } + + /** + * Cause this Activity to be recreated with a new instance. This results in + * essentially the same flow as when the Activity is created due to a + * configuration change -- the current instance will go through its + * lifecycle to onDestroy() and a new instance then created after it. + */ + @Override + public void recreate() { + //This SUCKS! Figure out a way to call the super method and support Android 1.6 + /* + if (IS_HONEYCOMB) { + super.recreate(); + } else { + */ + final Intent intent = getIntent(); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + + startActivity(intent); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) { + OverridePendingTransition.invoke(this); + } + + finish(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) { + OverridePendingTransition.invoke(this); + } + /* + } + */ + } + + private static final class OverridePendingTransition { + static void invoke(Activity activity) { + activity.overridePendingTransition(0, 0); + } + } + + /** + * Retain all appropriate fragment and loader state. You can NOT + * override this yourself! Use {@link #onRetainCustomNonConfigurationInstance()} + * if you want to retain your own state. + */ + @Override + public final Object onRetainNonConfigurationInstance() { + if (mStopped) { + doReallyStop(true); + } + + Object custom = onRetainCustomNonConfigurationInstance(); + + ArrayList fragments = mFragments.retainNonConfig(); + boolean retainLoaders = false; + if (mAllLoaderManagers != null) { + // prune out any loader managers that were already stopped and so + // have nothing useful to retain. + for (int i=mAllLoaderManagers.size()-1; i>=0; i--) { + LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i); + if (lm.mRetaining) { + retainLoaders = true; + } else { + lm.doDestroy(); + mAllLoaderManagers.removeAt(i); + } + } + } + if (fragments == null && !retainLoaders && custom == null) { + return null; + } + + NonConfigurationInstances nci = new NonConfigurationInstances(); + nci.activity = null; + nci.custom = custom; + nci.children = null; + nci.fragments = fragments; + nci.loaders = mAllLoaderManagers; + return nci; + } + + /** + * Save all appropriate fragment state. + */ + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + Parcelable p = mFragments.saveAllState(); + if (p != null) { + outState.putParcelable(FRAGMENTS_TAG, p); + } + } + + /** + * Dispatch onStart() to all fragments. Ensure any created loaders are + * now started. + */ + @Override + protected void onStart() { + super.onStart(); + + mStopped = false; + mReallyStopped = false; + mHandler.removeMessages(MSG_REALLY_STOPPED); + + if (!mCreated) { + mCreated = true; + ensureSupportActionBarAttached(); //Needed for retained fragments + mFragments.dispatchActivityCreated(); + } + + mFragments.noteStateNotSaved(); + mFragments.execPendingActions(); + + if (!mLoadersStarted) { + mLoadersStarted = true; + if (mLoaderManager != null) { + mLoaderManager.doStart(); + } else if (!mCheckedForLoaderManager) { + mLoaderManager = getLoaderManager(-1, mLoadersStarted, false); + } + mCheckedForLoaderManager = true; + } + // NOTE: HC onStart goes here. + + mFragments.dispatchStart(); + if (mAllLoaderManagers != null) { + for (int i=mAllLoaderManagers.size()-1; i>=0; i--) { + LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i); + lm.finishRetain(); + lm.doReportStart(); + } + } + } + + /** + * Dispatch onStop() to all fragments. Ensure all loaders are stopped. + */ + @Override + protected void onStop() { + super.onStop(); + + mStopped = true; + mHandler.sendEmptyMessage(MSG_REALLY_STOPPED); + + mFragments.dispatchStop(); + } + + /** + *

Sets the visibility of the indeterminate progress bar in the + * title.

+ * + *

In order for the progress bar to be shown, the feature must be + * requested via {@link #requestWindowFeature(long)}.

+ * + *

This method must be used instead of + * {@link #setProgressBarIndeterminateVisibility(boolean)} for + * ActionBarSherlock. Pass {@link Boolean.TRUE} or + * {@link Boolean.FALSE} to ensure the appropriate one is called.

+ * + * @param visible Whether to show the progress bars in the title. + */ + @Override + public void setProgressBarIndeterminateVisibility(Boolean visible) { + if (IS_HONEYCOMB || (getSupportActionBar() == null)) { + super.setProgressBarIndeterminateVisibility(visible); + } else if ((mWindowFlags & WINDOW_FLAG_INDETERMINANTE_PROGRESS) == WINDOW_FLAG_INDETERMINANTE_PROGRESS) { + ((ActionBarImpl)mActionBar).setProgressBarIndeterminateVisibility(visible); + } + } + + // ------------------------------------------------------------------------ + // NEW METHODS + // ------------------------------------------------------------------------ + + /** + * Use this instead of {@link #onRetainNonConfigurationInstance()}. + * Retrieve later with {@link #getLastCustomNonConfigurationInstance()}. + */ + public Object onRetainCustomNonConfigurationInstance() { + return null; + } + + /** + * Return the value previously returned from + * {@link #onRetainCustomNonConfigurationInstance()}. + */ + public Object getLastCustomNonConfigurationInstance() { + NonConfigurationInstances nc = (NonConfigurationInstances) + getLastNonConfigurationInstance(); + return nc != null ? nc.custom : null; + } + + /** + * @deprecated Use {@link invalidateOptionsMenu}. + */ + @Deprecated + void supportInvalidateOptionsMenu() { + invalidateOptionsMenu(); + } + + /** + * Print the Activity's state into the given stream. This gets invoked if + * you run "adb shell dumpsys activity ". + * + * @param prefix Desired prefix to prepend at each line of output. + * @param fd The raw file descriptor that the dump is being sent to. + * @param writer The PrintWriter to which you should dump your state. This will be + * closed for you after you return. + * @param args additional arguments to the dump request. + */ + @Override + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + if (IS_HONEYCOMB) { + //This can only work if we can call the super-class impl. :/ + //ActivityCompatHoneycomb.dump(this, prefix, fd, writer, args); + } + writer.print(prefix); writer.print("Local FragmentActivity "); + writer.print(Integer.toHexString(System.identityHashCode(this))); + writer.println(" State:"); + String innerPrefix = prefix + " "; + writer.print(innerPrefix); writer.print("mCreated="); + writer.print(mCreated); writer.print("mResumed="); + writer.print(mResumed); writer.print(" mStopped="); + writer.print(mStopped); writer.print(" mReallyStopped="); + writer.println(mReallyStopped); + writer.print(innerPrefix); writer.print("mLoadersStarted="); + writer.println(mLoadersStarted); + if (mLoaderManager != null) { + writer.print(prefix); writer.print("Loader Manager "); + writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager))); + writer.println(":"); + mLoaderManager.dump(prefix + " ", fd, writer, args); + } + mFragments.dump(prefix, fd, writer, args); + } + + void doReallyStop(boolean retaining) { + if (!mReallyStopped) { + mReallyStopped = true; + mRetaining = retaining; + mHandler.removeMessages(MSG_REALLY_STOPPED); + onReallyStop(); + } + } + + /** + * Pre-HC, we didn't have a way to determine whether an activity was + * being stopped for a config change or not until we saw + * onRetainNonConfigurationInstance() called after onStop(). However + * we need to know this, to know whether to retain fragments. This will + * tell us what we need to know. + */ + void onReallyStop() { + if (mLoadersStarted) { + mLoadersStarted = false; + if (mLoaderManager != null) { + if (!mRetaining) { + mLoaderManager.doStop(); + } else { + mLoaderManager.doRetain(); + } + } + } + + mFragments.dispatchReallyStop(); + } + + // ------------------------------------------------------------------------ + // ACTION BAR AND ACTION MODE SUPPORT + // ------------------------------------------------------------------------ + + /** + * Retrieve a reference to this activity's action bar handler. + * + * @return The handler for the appropriate action bar, or null. + */ + @Override + public ActionBar getSupportActionBar() { + return (mActionBar != null) ? mActionBar.getPublicInstance() : null; + } + + /** + * Notifies the activity that an action mode has finished. Activity + * subclasses overriding this method should call the superclass + * implementation. + * + * @param mode The action mode that just finished. + */ + @Override + public void onActionModeFinished(ActionMode mode) { + } + + /** + * Notifies the Activity that an action mode has been started. Activity + * subclasses overriding this method should call the superclass + * implementation. + * + * @param mode The new action mode. + */ + @Override + public void onActionModeStarted(ActionMode mode) { + } + + /** + *

Give the Activity a chance to control the UI for an action mode + * requested by the system.

+ * + *

Note: If you are looking for a notification callback that an action + * mode has been started for this activity, see + * {@link #onActionModeStarted(ActionMode)}.

+ * + * @param callback The callback that should control the new action mode + * @return The new action mode, or null if the activity does not want to + * provide special handling for this action mode. (It will be handled by the + * system.) + */ + @Override + public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { + return null; + } + + /** + * 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 cancelled + * @see android.support.v4.view.ActionMode + */ + @Override + public final ActionMode startActionMode(final ActionMode.Callback callback) { + //Give the activity override a chance to handle the action mode + ActionMode actionMode = onWindowStartingActionMode(callback); + + if (actionMode == null) { + //If the activity did not handle, send to action bar for platform- + //specific implementation + actionMode = mActionBar.startActionMode(callback); + } + if (actionMode != null) { + //Send the activity callback that our action mode was started + onActionModeStarted(actionMode); + } + + //Return to the caller + return actionMode; + } + + // ------------------------------------------------------------------------ + // FRAGMENT SUPPORT + // ------------------------------------------------------------------------ + + /** + * Called when a fragment is attached to the activity. + */ + @Override + public void onAttachFragment(Fragment fragment) { + } + + /** + * Return the FragmentManager for interacting with fragments associated + * with this activity. + */ + @Override + public FragmentManager getSupportFragmentManager() { + return mFragments; + } + + /** + * Modifies the standard behavior to allow results to be delivered to fragments. + * This imposes a restriction that requestCode be <= 0xffff. + */ + @Override + public void startActivityForResult(Intent intent, int requestCode) { + if (requestCode != -1 && (requestCode&0xffff0000) != 0) { + throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); + } + super.startActivityForResult(intent, requestCode); + } + + /** + * Called by Fragment.startActivityForResult() to implement its behavior. + */ + @Override + public void startActivityFromFragment(Fragment fragment, Intent intent, + int requestCode) { + if (requestCode == -1) { + super.startActivityForResult(intent, -1); + return; + } + if ((requestCode&0xffff0000) != 0) { + throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); + } + super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff)); + } + + void invalidateSupportFragmentIndex(int index) { + //Log.v(TAG, "invalidateFragmentIndex: index=" + index); + if (mAllLoaderManagers != null) { + LoaderManagerImpl lm = mAllLoaderManagers.get(index); + if (lm != null && !lm.mRetaining) { + lm.doDestroy(); + mAllLoaderManagers.remove(index); + } + } + } + + // ------------------------------------------------------------------------ + // LOADER SUPPORT + // ------------------------------------------------------------------------ + + /** + * Return the LoaderManager for this fragment, creating it if needed. + */ + @Override + public LoaderManager getSupportLoaderManager() { + if (mLoaderManager != null) { + return mLoaderManager; + } + mCheckedForLoaderManager = true; + mLoaderManager = getLoaderManager(-1, mLoadersStarted, true); + return mLoaderManager; + } + + LoaderManagerImpl getLoaderManager(int index, boolean started, boolean create) { + if (mAllLoaderManagers == null) { + mAllLoaderManagers = new HCSparseArray(); + } + LoaderManagerImpl lm = mAllLoaderManagers.get(index); + if (lm == null) { + if (create) { + lm = new LoaderManagerImpl(this, started); + mAllLoaderManagers.put(index, lm); + } + } else { + lm.updateActivity(this); + } + return lm; + } +} diff --git a/actionbarsherlock/plugins/maps/update.py b/actionbarsherlock/plugins/maps/update.py new file mode 100644 index 000000000..c3781c3a8 --- /dev/null +++ b/actionbarsherlock/plugins/maps/update.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +import os +import re + +dir_maps_base = os.path.dirname(os.path.realpath(__file__)) +dir_repo_base = os.path.dirname(os.path.dirname(dir_maps_base)) +dir_lib_base = os.path.join(dir_repo_base, 'library') + +path_to_source_activity = 'src/android/support/v4/app/FragmentActivity.java'.split('/') +path_to_dest_activity = 'src/android/support/v4/app/FragmentMapActivity.java'.split('/') + +file_source = os.path.join(dir_lib_base , *path_to_source_activity) +file_dest = os.path.join(dir_maps_base, *path_to_dest_activity) + +# Read in entire source file +code = None +with open(file_source) as f: + code = f.read() + + +code = code.split('\n') + +# Add MapActivity import +code.insert(31, 'import com.google.android.maps.MapActivity;') + +code = '\n'.join(code) + +# Class declaration +code = code.replace('class FragmentActivity extends Activity', 'abstract class FragmentMapActivity extends MapActivity') + +# TAG variable content +code = code.replace('"FragmentActivity"', '"FragmentMapActivity"') + +# Update inner class references +code = code.replace('FragmentActivity.this', 'FragmentMapActivity.this') + +# Class constructor +code = code.replace('FragmentActivity()', 'FragmentMapActivity()') + +# Javadoc +code = code.replace('Fragment, and Loader APIs.', 'Fragment, Loader, and Google Map APIs.') + + +# Exit stage left +with open(file_dest, 'w') as f: + f.write(code) diff --git a/actionbarsherlock/plugins/pom.xml b/actionbarsherlock/plugins/pom.xml new file mode 100644 index 000000000..b9957d6a4 --- /dev/null +++ b/actionbarsherlock/plugins/pom.xml @@ -0,0 +1,22 @@ + + + + 4.0.0 + + com.actionbarsherlock + parent-plugins + Plugins (Parent) + pom + + + com.actionbarsherlock + parent + 3.4.2 + ../pom.xml + + + + maps + preference + + diff --git a/actionbarsherlock/plugins/preference/AndroidManifest.xml b/actionbarsherlock/plugins/preference/AndroidManifest.xml new file mode 100644 index 000000000..8615e26fa --- /dev/null +++ b/actionbarsherlock/plugins/preference/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/actionbarsherlock/plugins/preference/README.md b/actionbarsherlock/plugins/preference/README.md new file mode 100644 index 000000000..256445d04 --- /dev/null +++ b/actionbarsherlock/plugins/preference/README.md @@ -0,0 +1,5 @@ +ActionBarSherlock Plugin: Preference +==================================== + +This plugin provides support for an action bar enhanced preference screen +through an additional base activity, `SherlockPreferenceActivity`. diff --git a/actionbarsherlock/plugins/preference/checkstyle.xml b/actionbarsherlock/plugins/preference/checkstyle.xml new file mode 100644 index 000000000..91bcb9a52 --- /dev/null +++ b/actionbarsherlock/plugins/preference/checkstyle.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/plugins/preference/pom.xml b/actionbarsherlock/plugins/preference/pom.xml new file mode 100644 index 000000000..6077bbcd5 --- /dev/null +++ b/actionbarsherlock/plugins/preference/pom.xml @@ -0,0 +1,69 @@ + + + + 4.0.0 + + com.actionbarsherlock + plugin-preference + Plugin: Preference + jar + + + com.actionbarsherlock + parent-plugins + 3.4.2 + ../pom.xml + + + + + android + android + provided + + + + com.actionbarsherlock + library + ${project.version} + provided + + + + + ${apk.prefix}-${project.artifactId}-${project.version} + src + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + ${project.basedir}/checkstyle.xml + + + + verify + + checkstyle + + + + + + + diff --git a/actionbarsherlock/plugins/preference/project.properties b/actionbarsherlock/plugins/preference/project.properties new file mode 100644 index 000000000..c632419fc --- /dev/null +++ b/actionbarsherlock/plugins/preference/project.properties @@ -0,0 +1,13 @@ +# 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-13 +android.library.reference.1=../../library diff --git a/actionbarsherlock/plugins/preference/src/android/support/v4/app/SherlockPreferenceActivity.java b/actionbarsherlock/plugins/preference/src/android/support/v4/app/SherlockPreferenceActivity.java new file mode 100644 index 000000000..c2ab8c5c2 --- /dev/null +++ b/actionbarsherlock/plugins/preference/src/android/support/v4/app/SherlockPreferenceActivity.java @@ -0,0 +1,1228 @@ +/* + * Copyright (C) 2011 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 android.support.v4.app; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import com.actionbarsherlock.R; +import com.actionbarsherlock.internal.app.ActionBarImpl; +import com.actionbarsherlock.internal.app.ActionBarWrapper; +import com.actionbarsherlock.internal.view.menu.MenuBuilder; +import com.actionbarsherlock.internal.view.menu.MenuInflaterWrapper; +import com.actionbarsherlock.internal.view.menu.MenuItemImpl; +import com.actionbarsherlock.internal.view.menu.MenuItemWrapper; +import com.actionbarsherlock.internal.view.menu.MenuWrapper; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Parcelable; +import android.preference.PreferenceActivity; +import android.support.v4.view.ActionMode; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuInflater; +import android.support.v4.view.MenuItem; +import android.support.v4.view.Window; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; + +/** + * Base class for activities that want to use the support-based ActionBar, + * Fragment, Loader, and Preference APIs. + * + *

Known limitations:

+ *
    + *
  • When using the <fragment> tag, this implementation can not + * use the parent view's ID as the new fragment's ID. You must explicitly + * specify an ID (or tag) in the <fragment>.

    + *
  • Prior to Honeycomb (3.0), an activity's state was saved before pausing. + * Fragments are a significant amount of new state, and dynamic enough that one + * often wants them to change between pausing and stopping. These classes + * throw an exception if you try to change the fragment state after it has been + * saved, to avoid accidental loss of UI state. However this is too restrictive + * prior to Honeycomb, where the state is saved before pausing. To address this, + * when running on platforms prior to Honeycomb an exception will not be thrown + * if you change fragments between the state save and the activity being stopped. + * This means that is some cases if the activity is restored from its last saved + * state, this may be a snapshot slightly before what the user last saw.

    + *
+ */ +public abstract class SherlockPreferenceActivity extends PreferenceActivity implements SupportActivity { + private static final String TAG = "SherlockPreferenceActivity"; + private static final boolean DEBUG = false; + + private static final String FRAGMENTS_TAG = "android:support:fragments"; + + static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; + + static final int MSG_REALLY_STOPPED = 1; + static final int MSG_RESUME_PENDING = 2; + + private static final int WINDOW_FLAG_ACTION_BAR = 1 << Window.FEATURE_ACTION_BAR; + private static final int WINDOW_FLAG_ACTION_BAR_ITEM_TEXT = 1 << Window.FEATURE_ACTION_BAR_ITEM_TEXT; + private static final int WINDOW_FLAG_ACTION_BAR_OVERLAY = 1 << Window.FEATURE_ACTION_BAR_OVERLAY; + private static final int WINDOW_FLAG_ACTION_MODE_OVERLAY = 1 << Window.FEATURE_ACTION_MODE_OVERLAY; + private static final int WINDOW_FLAG_INDETERMINANTE_PROGRESS = 1 << Window.FEATURE_INDETERMINATE_PROGRESS; + + final SupportActivity.InternalCallbacks mInternalCallbacks = new SupportActivity.InternalCallbacks() { + @Override + void invalidateSupportFragmentIndex(int index) { + SherlockPreferenceActivity.this.invalidateSupportFragmentIndex(index); + } + + @Override + LoaderManagerImpl getLoaderManager(int index, boolean started, boolean create) { + return SherlockPreferenceActivity.this.getLoaderManager(index, started, create); + } + + @Override + Handler getHandler() { + return mHandler; + } + + @Override + FragmentManagerImpl getFragments() { + return mFragments; + } + + @Override + void ensureSupportActionBarAttached() { + SherlockPreferenceActivity.this.ensureSupportActionBarAttached(); + } + + @Override + boolean getRetaining() { + return mRetaining; + } + }; + + final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_REALLY_STOPPED: + if (mStopped) { + doReallyStop(false); + } + break; + case MSG_RESUME_PENDING: + mFragments.dispatchResume(); + mFragments.execPendingActions(); + break; + default: + super.handleMessage(msg); + } + } + }; + final FragmentManagerImpl mFragments = new FragmentManagerImpl(); + + ActionBar mActionBar; + boolean mIsActionBarImplAttached; + long mWindowFlags = 0; + + final MenuBuilder mSupportMenu; + final MenuBuilder.Callback mSupportMenuCallback = new MenuBuilder.Callback() { + @Override + public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { + return SherlockPreferenceActivity.this.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item); + } + }; + + boolean mCreated; + boolean mResumed; + boolean mStopped; + boolean mReallyStopped; + boolean mRetaining; + + boolean mIsEnsureInflating = false; + + boolean mOptionsMenuInvalidated; + boolean mOptionsMenuCreateResult; + + boolean mCheckedForLoaderManager; + boolean mLoadersStarted; + HCSparseArray mAllLoaderManagers; + LoaderManagerImpl mLoaderManager; + + static final class NonConfigurationInstances { + Object activity; + Object custom; + HashMap children; + ArrayList fragments; + HCSparseArray loaders; + } + + static class FragmentTag { + public static final int[] Fragment = { + 0x01010003, 0x010100d0, 0x010100d1 + }; + public static final int Fragment_id = 1; + public static final int Fragment_name = 0; + public static final int Fragment_tag = 2; + } + + + + public SherlockPreferenceActivity() { + super(); + + if (IS_HONEYCOMB) { + mActionBar = ActionBarWrapper.createFor(this); + mSupportMenu = null; //Everything should be done natively + } else { + mSupportMenu = new MenuBuilder(this); + mSupportMenu.setCallback(mSupportMenuCallback); + } + } + + @Override + public SupportActivity.InternalCallbacks getInternalCallbacks() { + return mInternalCallbacks; + } + + @Override + public Activity asActivity() { + return this; + } + + protected void ensureSupportActionBarAttached() { + if (IS_HONEYCOMB || mIsActionBarImplAttached) { + return; + } + if (!isChild()) { + //Get the list view that the parent attached + final ListView contentView = (ListView)getWindow().getDecorView().findViewById(android.R.id.list); + ((ViewGroup)contentView.getParent()).removeView(contentView); + + //Disable the callback in PrefrenceActivity + mIsEnsureInflating = true; + + if ((mWindowFlags & WINDOW_FLAG_ACTION_BAR) == WINDOW_FLAG_ACTION_BAR) { + if ((mWindowFlags & WINDOW_FLAG_ACTION_BAR_OVERLAY) == WINDOW_FLAG_ACTION_BAR_OVERLAY) { + super.setContentView(R.layout.abs__screen_action_bar_overlay); + } else { + super.setContentView(R.layout.abs__screen_action_bar); + } + + mActionBar = new ActionBarImpl(this); + ((ActionBarImpl)mActionBar).init(); + + final boolean textEnabled = ((mWindowFlags & WINDOW_FLAG_ACTION_BAR_ITEM_TEXT) == WINDOW_FLAG_ACTION_BAR_ITEM_TEXT); + mSupportMenu.setShowsActionItemText(textEnabled); + + if ((mWindowFlags & WINDOW_FLAG_INDETERMINANTE_PROGRESS) == WINDOW_FLAG_INDETERMINANTE_PROGRESS) { + ((ActionBarImpl)mActionBar).setProgressBarIndeterminateVisibility(false); + } + + //TODO set other flags + } else { + if ((mWindowFlags & WINDOW_FLAG_INDETERMINANTE_PROGRESS) == WINDOW_FLAG_INDETERMINANTE_PROGRESS) { + super.requestWindowFeature((int)Window.FEATURE_INDETERMINATE_PROGRESS); + } + super.setContentView(R.layout.abs__screen_simple); + } + + //Attach original list view to the new layout + ((ViewGroup)findViewById(R.id.abs__content)).addView(contentView); + + //Re-enable the callback in PreferenceActivity + mIsEnsureInflating = false; + super.onContentChanged(); + } + + invalidateOptionsMenu(); + mIsActionBarImplAttached = true; + } + + @Override + public void onContentChanged() { + if (!mIsEnsureInflating) { + super.onContentChanged(); + } + } + + // ------------------------------------------------------------------------ + // HOOKS INTO ACTIVITY + // ------------------------------------------------------------------------ + + /** + * Enable extended window features. + * + * @param featureId The desired feature as defined in + * {@link android.support.v4.view.Window}. + * @return Returns {@code true} if the requested feature is supported and + * now enabled. + */ + @Override + public boolean requestWindowFeature(long featureId) { + if (!IS_HONEYCOMB) { + switch ((int)featureId) { + case (int)Window.FEATURE_ACTION_BAR: + case (int)Window.FEATURE_ACTION_BAR_ITEM_TEXT: + case (int)Window.FEATURE_ACTION_BAR_OVERLAY: + case (int)Window.FEATURE_ACTION_MODE_OVERLAY: + case (int)Window.FEATURE_INDETERMINATE_PROGRESS: + mWindowFlags |= (1 << featureId); + return true; + } + } + return super.requestWindowFeature((int)featureId); + } + + @Override + public android.view.MenuInflater getMenuInflater() { + if (IS_HONEYCOMB) { + if (DEBUG) Log.d(TAG, "getMenuInflater(): Wrapping native inflater."); + + //Wrap the native inflater so it can unwrap the native menu first + return new MenuInflaterWrapper(this, super.getMenuInflater()); + } + + if (DEBUG) Log.d(TAG, "getMenuInflater(): Returning support inflater."); + + //Use our custom menu inflater + return new MenuInflater(this, super.getMenuInflater()); + } + + @Override + public void setTitle(CharSequence title) { + if (IS_HONEYCOMB || (getSupportActionBar() == null)) { + super.setTitle(title); + } else { + getSupportActionBar().setTitle(title); + } + } + + @Override + public void setTitle(int titleId) { + if (IS_HONEYCOMB || (getSupportActionBar() == null)) { + super.setTitle(titleId); + } else { + getSupportActionBar().setTitle(titleId); + } + } + + /** + * Dispatch incoming result to the correct fragment. + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + int index = requestCode>>16; + if (index != 0) { + index--; + if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) { + Log.w(TAG, "Activity result fragment index out of range: 0x" + + Integer.toHexString(requestCode)); + return; + } + Fragment frag = mFragments.mActive.get(index); + if (frag == null) { + Log.w(TAG, "Activity result no fragment exists for index: 0x" + + Integer.toHexString(requestCode)); + } else { + frag.onActivityResult(requestCode&0xffff, resultCode, data); + } + return; + } + + super.onActivityResult(requestCode, resultCode, data); + } + + @Override + protected void onApplyThemeResource(Theme theme, int resid, boolean first) { + TypedArray attrs = theme.obtainStyledAttributes(resid, R.styleable.SherlockTheme); + + final boolean actionBar = attrs.getBoolean(R.styleable.SherlockTheme_windowActionBar, false); + mWindowFlags |= actionBar ? WINDOW_FLAG_ACTION_BAR : 0; + + final boolean actionModeOverlay = attrs.getBoolean(R.styleable.SherlockTheme_windowActionModeOverlay, false); + mWindowFlags |= actionModeOverlay ? WINDOW_FLAG_ACTION_MODE_OVERLAY : 0; + + attrs.recycle(); + super.onApplyThemeResource(theme, resid, first); + } + + /** + * Take care of popping the fragment back stack or finishing the activity + * as appropriate. + */ + @Override + public void onBackPressed() { + if (!mFragments.popBackStackImmediate()) { + finish(); + } + } + + /** + * Dispatch configuration change to all fragments. + */ + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mFragments.dispatchConfigurationChanged(newConfig); + } + + /** + * Perform initialization of all fragments and loaders. + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + mFragments.attachActivity(this); + // Old versions of the platform didn't do this! + if (getLayoutInflater().getFactory() == null) { + getLayoutInflater().setFactory(this); + } + + super.onCreate(savedInstanceState); + + NonConfigurationInstances nc = (NonConfigurationInstances) + getLastNonConfigurationInstance(); + if (nc != null) { + mAllLoaderManagers = nc.loaders; + } + if (savedInstanceState != null) { + Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); + mFragments.restoreAllState(p, nc != null ? nc.fragments : null); + } + ensureSupportActionBarAttached(); + mFragments.dispatchCreate(); + } + + /** + *

Initialize the contents of the Activity's standard options menu. You + * should place your menu items in to menu.

+ * + *

The default implementation populates the menu with standard system + * menu items. These are placed in the {@link Menu.CATEGORY_SYSTEM} group + * so that they will be correctly ordered with application-defined menu + * items. Deriving classes should always call through to the base + * implementation.

+ * + *

You can safely hold on to menu (and any items created from it), + * making modifications to it as desired, until the next time + * {@code onCreateOptionsMenu()} is called.

+ * + *

When you add items to the menu, you can implement the Activity's + * {@link #onOptionsItemSelected(MenuItem)} method to handle them + * there.

+ * + * @param menu The options menu in which you place your items. + * @return You must return true for the menu to be displayed; if you return + * false it will not be shown. + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu(Menu): Returning " + menu.hasVisibleItems()); + return menu.hasVisibleItems(); + } + + @Override + public final boolean onCreateOptionsMenu(android.view.Menu menu) { + // Prior to Honeycomb, the framework can't invalidate the options + // menu, so we must always say we have one in case the app later + // invalidates it and needs to have it shown. + boolean result = true; + + if (IS_HONEYCOMB) { + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu(android.view.Menu): Calling support method with wrapped native menu."); + MenuWrapper wrapped = new MenuWrapper(menu); + result = onCreateOptionsMenu(wrapped); + result |= mFragments.dispatchCreateOptionsMenu(wrapped, getMenuInflater()); + } + + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu(android.view.Menu): Returning " + result); + return result; + } + + /** + * Add support for inflating the <fragment> tag. + */ + @Override + public View onCreateView(String name, Context context, AttributeSet attrs) { + if (!"fragment".equals(name)) { + return super.onCreateView(name, context, attrs); + } + + String fname = attrs.getAttributeValue(null, "class"); + TypedArray a = context.obtainStyledAttributes(attrs, FragmentTag.Fragment); + if (fname == null) { + fname = a.getString(FragmentTag.Fragment_name); + } + int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID); + String tag = a.getString(FragmentTag.Fragment_tag); + a.recycle(); + + View parent = null; // NOTE: no way to get parent pre-Honeycomb. + int containerId = parent != null ? parent.getId() : 0; + if (containerId == View.NO_ID && id == View.NO_ID && tag == null) { + throw new IllegalArgumentException(attrs.getPositionDescription() + + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname); + } + + // If we restored from a previous state, we may already have + // instantiated this fragment from the state and should use + // that instance instead of making a new one. + Fragment fragment = id != View.NO_ID ? mFragments.findFragmentById(id) : null; + if (fragment == null && tag != null) { + fragment = mFragments.findFragmentByTag(tag); + } + if (fragment == null && containerId != View.NO_ID) { + fragment = mFragments.findFragmentById(containerId); + } + + if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x" + + Integer.toHexString(id) + " fname=" + fname + + " existing=" + fragment); + if (fragment == null) { + fragment = Fragment.instantiate(this, fname); + fragment.mFromLayout = true; + fragment.mFragmentId = id != 0 ? id : containerId; + fragment.mContainerId = containerId; + fragment.mTag = tag; + fragment.mInLayout = true; + fragment.mFragmentManager = mFragments; + fragment.onInflate(this, attrs, fragment.mSavedFragmentState); + mFragments.addFragment(fragment, true); + + } else if (fragment.mInLayout) { + // A fragment already exists and it is not one we restored from + // previous state. + throw new IllegalArgumentException(attrs.getPositionDescription() + + ": Duplicate id 0x" + Integer.toHexString(id) + + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId) + + " with another fragment for " + fname); + } else { + // This fragment was retained from a previous instance; get it + // going now. + fragment.mInLayout = true; + // If this fragment is newly instantiated (either right now, or + // from last saved state), then give it the attributes to + // initialize itself. + if (!fragment.mRetaining) { + fragment.onInflate(this, attrs, fragment.mSavedFragmentState); + } + mFragments.moveToState(fragment); + } + + if (fragment.mView == null) { + throw new IllegalStateException("Fragment " + fname + + " did not create a view."); + } + if (id != 0) { + fragment.mView.setId(id); + } + if (fragment.mView.getTag() == null) { + fragment.mView.setTag(tag); + } + return fragment.mView; + } + + @Override + public void invalidateOptionsMenu() { + if (DEBUG) Log.d(TAG, "supportInvalidateOptionsMenu(): Invalidating menu."); + + if (IS_HONEYCOMB) { + HoneycombInvalidateOptionsMenu.invoke(this); + } else { + mSupportMenu.clear(); + + mOptionsMenuCreateResult = onCreateOptionsMenu(mSupportMenu); + mOptionsMenuCreateResult |= mFragments.dispatchCreateOptionsMenu(mSupportMenu, getMenuInflater()); + + if (getSupportActionBar() != null) { + if (onPrepareOptionsMenu(mSupportMenu)) { + mFragments.dispatchPrepareOptionsMenu(mSupportMenu); + } + + //Since we now know we are using a custom action bar, perform the + //inflation callback to allow it to display any items it wants. + ((ActionBarImpl)mActionBar).onMenuInflated(mSupportMenu); + } + + // Whoops, older platform... we'll use a hack, to manually rebuild + // the options menu the next time it is prepared. + mOptionsMenuInvalidated = true; + } + } + + private static final class HoneycombInvalidateOptionsMenu { + static void invoke(Activity activity) { + activity.getWindow().invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); + } + } + + /** + * Destroy all fragments and loaders. + */ + @Override + protected void onDestroy() { + super.onDestroy(); + + doReallyStop(false); + + mFragments.dispatchDestroy(); + if (mLoaderManager != null) { + mLoaderManager.doDestroy(); + } + } + + /** + * Take care of calling onBackPressed() for pre-Eclair platforms. + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (android.os.Build.VERSION.SDK_INT < 5 /* ECLAIR */ + && keyCode == KeyEvent.KEYCODE_BACK + && event.getRepeatCount() == 0) { + // Take care of calling this method on earlier versions of + // the platform where it doesn't exist. + onBackPressed(); + return true; + } + + return super.onKeyDown(keyCode, event); + } + + /** + * Dispatch onLowMemory() to all fragments. + */ + @Override + public void onLowMemory() { + super.onLowMemory(); + mFragments.dispatchLowMemory(); + } + + /** + * Dispatch context and options menu to fragments. + */ + @Override + public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) { + if (super.onMenuItemSelected(featureId, item)) { + return true; + } + + switch (featureId) { + case Window.FEATURE_OPTIONS_PANEL: + return mFragments.dispatchOptionsItemSelected(new MenuItemWrapper(item)); + + case Window.FEATURE_CONTEXT_MENU: + return mFragments.dispatchContextItemSelected(new MenuItemWrapper(item)); + + default: + return false; + } + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + if (onOptionsItemSelected(item)) { + return true; + } + + switch (featureId) { + case Window.FEATURE_OPTIONS_PANEL: + return mFragments.dispatchOptionsItemSelected(item); + + case Window.FEATURE_CONTEXT_MENU: + return mFragments.dispatchContextItemSelected(item); + + default: + return false; + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return super.onOptionsItemSelected(item); + } + + @Override + public final boolean onOptionsItemSelected(android.view.MenuItem item) { + return onOptionsItemSelected(new MenuItemWrapper(item)); + } + + /** + * Call onOptionsMenuClosed() on fragments. + */ + @Override + public void onPanelClosed(int featureId, android.view.Menu menu) { + switch (featureId) { + case Window.FEATURE_OPTIONS_PANEL: + mFragments.dispatchOptionsMenuClosed(new MenuWrapper(menu)); + + if (!IS_HONEYCOMB && (getSupportActionBar() != null)) { + if (DEBUG) Log.d(TAG, "onPanelClosed(int, android.view.Menu): Dispatch menu visibility false to custom action bar."); + ((ActionBarImpl)mActionBar).onMenuVisibilityChanged(false); + } + break; + } + super.onPanelClosed(featureId, menu); + } + + /** + * Dispatch onPause() to fragments. + */ + @Override + protected void onPause() { + super.onPause(); + mResumed = false; + if (mHandler.hasMessages(MSG_RESUME_PENDING)) { + mHandler.removeMessages(MSG_RESUME_PENDING); + mFragments.dispatchResume(); + } + mFragments.dispatchPause(); + } + + /** + * Dispatch onResume() to fragments. + */ + @Override + protected void onResume() { + super.onResume(); + mHandler.sendEmptyMessage(MSG_RESUME_PENDING); + mResumed = true; + mFragments.execPendingActions(); + } + + /** + * Dispatch onResume() to fragments. + */ + @Override + protected void onPostResume() { + super.onPostResume(); + mHandler.removeMessages(MSG_RESUME_PENDING); + mFragments.dispatchResume(); + mFragments.execPendingActions(); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + boolean result = menu.hasVisibleItems(); + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(Menu): Returning " + result); + return result; + } + + @Override + public final boolean onPrepareOptionsMenu(android.view.Menu menu) { + boolean result = super.onPrepareOptionsMenu(menu); + + if (!IS_HONEYCOMB) { + if (DEBUG) { + Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): mOptionsMenuCreateResult = " + mOptionsMenuCreateResult); + Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): mOptionsMenuInvalidated = " + mOptionsMenuInvalidated); + } + + boolean prepareResult = true; + if (mOptionsMenuCreateResult) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Calling support method with custom menu."); + prepareResult = onPrepareOptionsMenu(mSupportMenu); + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Support method result returned " + prepareResult); + if (prepareResult) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Dispatching fragment method with custom menu."); + mFragments.dispatchPrepareOptionsMenu(mSupportMenu); + } + } + + if (mOptionsMenuInvalidated) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Clearing existing options menu."); + menu.clear(); + mOptionsMenuInvalidated = false; + + if (mOptionsMenuCreateResult && prepareResult) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Adding any action items that are not displayed on the action bar."); + //Only add items that have not already been added to our custom + //action bar implementation + for (MenuItemImpl item : mSupportMenu.getItems()) { + if (!item.isShownOnActionBar()) { + item.addTo(menu); + } + } + } + } + + if (mOptionsMenuCreateResult && prepareResult && menu.hasVisibleItems()) { + if (getSupportActionBar() != null) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Dispatch menu visibility true to custom action bar."); + ((ActionBarImpl)mActionBar).onMenuVisibilityChanged(true); + } + result = true; + } + } else { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Calling support method with wrapped native menu."); + final MenuWrapper wrappedMenu = new MenuWrapper(menu); + result = onPrepareOptionsMenu(wrappedMenu); + if (result) { + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Dispatching fragment method with wrapped native menu."); + mFragments.dispatchPrepareOptionsMenu(wrappedMenu); + } + } + + if (DEBUG) Log.d(TAG, "onPrepareOptionsMenu(android.view.Menu): Returning " + result); + return result; + } + + /** + * Cause this Activity to be recreated with a new instance. This results in + * essentially the same flow as when the Activity is created due to a + * configuration change -- the current instance will go through its + * lifecycle to onDestroy() and a new instance then created after it. + */ + @Override + public void recreate() { + //This SUCKS! Figure out a way to call the super method and support Android 1.6 + /* + if (IS_HONEYCOMB) { + super.recreate(); + } else { + */ + final Intent intent = getIntent(); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + + startActivity(intent); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) { + OverridePendingTransition.invoke(this); + } + + finish(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) { + OverridePendingTransition.invoke(this); + } + /* + } + */ + } + + private static final class OverridePendingTransition { + static void invoke(Activity activity) { + activity.overridePendingTransition(0, 0); + } + } + + /** + * Retain all appropriate fragment and loader state. You can NOT + * override this yourself! Use {@link #onRetainCustomNonConfigurationInstance()} + * if you want to retain your own state. + */ + @Override + public final Object onRetainNonConfigurationInstance() { + if (mStopped) { + doReallyStop(true); + } + + Object custom = onRetainCustomNonConfigurationInstance(); + + ArrayList fragments = mFragments.retainNonConfig(); + boolean retainLoaders = false; + if (mAllLoaderManagers != null) { + // prune out any loader managers that were already stopped and so + // have nothing useful to retain. + for (int i=mAllLoaderManagers.size()-1; i>=0; i--) { + LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i); + if (lm.mRetaining) { + retainLoaders = true; + } else { + lm.doDestroy(); + mAllLoaderManagers.removeAt(i); + } + } + } + if (fragments == null && !retainLoaders && custom == null) { + return null; + } + + NonConfigurationInstances nci = new NonConfigurationInstances(); + nci.activity = null; + nci.custom = custom; + nci.children = null; + nci.fragments = fragments; + nci.loaders = mAllLoaderManagers; + return nci; + } + + /** + * Save all appropriate fragment state. + */ + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + Parcelable p = mFragments.saveAllState(); + if (p != null) { + outState.putParcelable(FRAGMENTS_TAG, p); + } + } + + /** + * Dispatch onStart() to all fragments. Ensure any created loaders are + * now started. + */ + @Override + protected void onStart() { + super.onStart(); + + mStopped = false; + mReallyStopped = false; + mHandler.removeMessages(MSG_REALLY_STOPPED); + + if (!mCreated) { + mCreated = true; + ensureSupportActionBarAttached(); //Needed for retained fragments + mFragments.dispatchActivityCreated(); + } + + mFragments.noteStateNotSaved(); + mFragments.execPendingActions(); + + if (!mLoadersStarted) { + mLoadersStarted = true; + if (mLoaderManager != null) { + mLoaderManager.doStart(); + } else if (!mCheckedForLoaderManager) { + mLoaderManager = getLoaderManager(-1, mLoadersStarted, false); + } + mCheckedForLoaderManager = true; + } + // NOTE: HC onStart goes here. + + mFragments.dispatchStart(); + if (mAllLoaderManagers != null) { + for (int i=mAllLoaderManagers.size()-1; i>=0; i--) { + LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i); + lm.finishRetain(); + lm.doReportStart(); + } + } + } + + /** + * Dispatch onStop() to all fragments. Ensure all loaders are stopped. + */ + @Override + protected void onStop() { + super.onStop(); + + mStopped = true; + mHandler.sendEmptyMessage(MSG_REALLY_STOPPED); + + mFragments.dispatchStop(); + } + + /** + *

Sets the visibility of the indeterminate progress bar in the + * title.

+ * + *

In order for the progress bar to be shown, the feature must be + * requested via {@link #requestWindowFeature(long)}.

+ * + *

This method must be used instead of + * {@link #setProgressBarIndeterminateVisibility(boolean)} for + * ActionBarSherlock. Pass {@link Boolean.TRUE} or + * {@link Boolean.FALSE} to ensure the appropriate one is called.

+ * + * @param visible Whether to show the progress bars in the title. + */ + @Override + public void setProgressBarIndeterminateVisibility(Boolean visible) { + if (IS_HONEYCOMB || (getSupportActionBar() == null)) { + super.setProgressBarIndeterminateVisibility(visible); + } else if ((mWindowFlags & WINDOW_FLAG_INDETERMINANTE_PROGRESS) == WINDOW_FLAG_INDETERMINANTE_PROGRESS) { + ((ActionBarImpl)mActionBar).setProgressBarIndeterminateVisibility(visible); + } + } + + // ------------------------------------------------------------------------ + // NEW METHODS + // ------------------------------------------------------------------------ + + /** + * Use this instead of {@link #onRetainNonConfigurationInstance()}. + * Retrieve later with {@link #getLastCustomNonConfigurationInstance()}. + */ + public Object onRetainCustomNonConfigurationInstance() { + return null; + } + + /** + * Return the value previously returned from + * {@link #onRetainCustomNonConfigurationInstance()}. + */ + public Object getLastCustomNonConfigurationInstance() { + NonConfigurationInstances nc = (NonConfigurationInstances) + getLastNonConfigurationInstance(); + return nc != null ? nc.custom : null; + } + + /** + * @deprecated Use {@link invalidateOptionsMenu}. + */ + @Deprecated + void supportInvalidateOptionsMenu() { + invalidateOptionsMenu(); + } + + /** + * Print the Activity's state into the given stream. This gets invoked if + * you run "adb shell dumpsys activity ". + * + * @param prefix Desired prefix to prepend at each line of output. + * @param fd The raw file descriptor that the dump is being sent to. + * @param writer The PrintWriter to which you should dump your state. This will be + * closed for you after you return. + * @param args additional arguments to the dump request. + */ + @Override + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + if (IS_HONEYCOMB) { + //This can only work if we can call the super-class impl. :/ + //ActivityCompatHoneycomb.dump(this, prefix, fd, writer, args); + } + writer.print(prefix); writer.print("Local FragmentActivity "); + writer.print(Integer.toHexString(System.identityHashCode(this))); + writer.println(" State:"); + String innerPrefix = prefix + " "; + writer.print(innerPrefix); writer.print("mCreated="); + writer.print(mCreated); writer.print("mResumed="); + writer.print(mResumed); writer.print(" mStopped="); + writer.print(mStopped); writer.print(" mReallyStopped="); + writer.println(mReallyStopped); + writer.print(innerPrefix); writer.print("mLoadersStarted="); + writer.println(mLoadersStarted); + if (mLoaderManager != null) { + writer.print(prefix); writer.print("Loader Manager "); + writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager))); + writer.println(":"); + mLoaderManager.dump(prefix + " ", fd, writer, args); + } + mFragments.dump(prefix, fd, writer, args); + } + + void doReallyStop(boolean retaining) { + if (!mReallyStopped) { + mReallyStopped = true; + mRetaining = retaining; + mHandler.removeMessages(MSG_REALLY_STOPPED); + onReallyStop(); + } + } + + /** + * Pre-HC, we didn't have a way to determine whether an activity was + * being stopped for a config change or not until we saw + * onRetainNonConfigurationInstance() called after onStop(). However + * we need to know this, to know whether to retain fragments. This will + * tell us what we need to know. + */ + void onReallyStop() { + if (mLoadersStarted) { + mLoadersStarted = false; + if (mLoaderManager != null) { + if (!mRetaining) { + mLoaderManager.doStop(); + } else { + mLoaderManager.doRetain(); + } + } + } + + mFragments.dispatchReallyStop(); + } + + // ------------------------------------------------------------------------ + // ACTION BAR AND ACTION MODE SUPPORT + // ------------------------------------------------------------------------ + + /** + * Retrieve a reference to this activity's action bar handler. + * + * @return The handler for the appropriate action bar, or null. + */ + @Override + public ActionBar getSupportActionBar() { + return (mActionBar != null) ? mActionBar.getPublicInstance() : null; + } + + /** + * Notifies the activity that an action mode has finished. Activity + * subclasses overriding this method should call the superclass + * implementation. + * + * @param mode The action mode that just finished. + */ + @Override + public void onActionModeFinished(ActionMode mode) { + } + + /** + * Notifies the Activity that an action mode has been started. Activity + * subclasses overriding this method should call the superclass + * implementation. + * + * @param mode The new action mode. + */ + @Override + public void onActionModeStarted(ActionMode mode) { + } + + /** + *

Give the Activity a chance to control the UI for an action mode + * requested by the system.

+ * + *

Note: If you are looking for a notification callback that an action + * mode has been started for this activity, see + * {@link #onActionModeStarted(ActionMode)}.

+ * + * @param callback The callback that should control the new action mode + * @return The new action mode, or null if the activity does not want to + * provide special handling for this action mode. (It will be handled by the + * system.) + */ + @Override + public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { + return null; + } + + /** + * 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 cancelled + * @see android.support.v4.view.ActionMode + */ + @Override + public final ActionMode startActionMode(final ActionMode.Callback callback) { + //Give the activity override a chance to handle the action mode + ActionMode actionMode = onWindowStartingActionMode(callback); + + if (actionMode == null) { + //If the activity did not handle, send to action bar for platform- + //specific implementation + actionMode = mActionBar.startActionMode(callback); + } + if (actionMode != null) { + //Send the activity callback that our action mode was started + onActionModeStarted(actionMode); + } + + //Return to the caller + return actionMode; + } + + // ------------------------------------------------------------------------ + // FRAGMENT SUPPORT + // ------------------------------------------------------------------------ + + /** + * Called when a fragment is attached to the activity. + */ + @Override + public void onAttachFragment(Fragment fragment) { + } + + /** + * Return the FragmentManager for interacting with fragments associated + * with this activity. + */ + @Override + public FragmentManager getSupportFragmentManager() { + return mFragments; + } + + /** + * Modifies the standard behavior to allow results to be delivered to fragments. + * This imposes a restriction that requestCode be <= 0xffff. + */ + @Override + public void startActivityForResult(Intent intent, int requestCode) { + if (requestCode != -1 && (requestCode&0xffff0000) != 0) { + throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); + } + super.startActivityForResult(intent, requestCode); + } + + /** + * Called by Fragment.startActivityForResult() to implement its behavior. + */ + @Override + public void startActivityFromFragment(Fragment fragment, Intent intent, + int requestCode) { + if (requestCode == -1) { + super.startActivityForResult(intent, -1); + return; + } + if ((requestCode&0xffff0000) != 0) { + throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); + } + super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff)); + } + + void invalidateSupportFragmentIndex(int index) { + //Log.v(TAG, "invalidateFragmentIndex: index=" + index); + if (mAllLoaderManagers != null) { + LoaderManagerImpl lm = mAllLoaderManagers.get(index); + if (lm != null && !lm.mRetaining) { + lm.doDestroy(); + mAllLoaderManagers.remove(index); + } + } + } + + // ------------------------------------------------------------------------ + // LOADER SUPPORT + // ------------------------------------------------------------------------ + + /** + * Return the LoaderManager for this fragment, creating it if needed. + */ + @Override + public LoaderManager getSupportLoaderManager() { + if (mLoaderManager != null) { + return mLoaderManager; + } + mCheckedForLoaderManager = true; + mLoaderManager = getLoaderManager(-1, mLoadersStarted, true); + return mLoaderManager; + } + + LoaderManagerImpl getLoaderManager(int index, boolean started, boolean create) { + if (mAllLoaderManagers == null) { + mAllLoaderManagers = new HCSparseArray(); + } + LoaderManagerImpl lm = mAllLoaderManagers.get(index); + if (lm == null) { + if (create) { + lm = new LoaderManagerImpl(this, started); + mAllLoaderManagers.put(index, lm); + } + } else { + lm.updateActivity(this); + } + return lm; + } +} diff --git a/actionbarsherlock/pom.xml b/actionbarsherlock/pom.xml new file mode 100644 index 000000000..99c923738 --- /dev/null +++ b/actionbarsherlock/pom.xml @@ -0,0 +1,213 @@ + + + + 4.0.0 + + com.actionbarsherlock + parent + pom + 3.4.2 + + ActionBarSherlock (Parent) + Android library for implementing the action bar design pattern using the native ActionBar on 3.0+ and a custom implementation on pre-3.0 all through the same API. + https://github.com/JakeWharton/ActionBarSherlock + 2011 + + + library + plugins + samples + test + + + + http://github.com/JakeWharton/ActionBarSherlock/ + scm:git:git://github.com/JakeWharton/ActionBarSherlock.git + scm:git:git@github.com:JakeWharton/ActionBarSherlock.git + + + + + Jake Wharton + jakewharton@gmail.com + jakewharton + http://jakewharton.com + -5 + + developer + + + + + + + Apache License Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + personal-repository + JakeWharton.com Maven Repository + scp://r.jakewharton.com/home/jakewharton_repository/r.jakewharton.com/maven/release/ + + + personal-repository + JakeWharton.com Maven Repository + scp://r.jakewharton.com/home/jakewharton_repository/r.jakewharton.com/maven/snapshot/ + + + github-project-site + GitHub Project Pages + gitsite:git@github.com/JakeWharton/ActionBarSherlock + + + + + + Jake Wharton + http://jakewharton.com + + + + GitHub Issues + https://github.com/JakeWharton/ActionBarSherlock/issues + + + + UTF-8 + UTF-8 + + actionbarsherlock + + 1.6 + 3.2_r1 + 13 + 13_r1 + + + + + + android + android + ${android.version} + + + + com.google.android.maps + maps + ${android.maps.version} + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + ${java.version} + ${java.version} + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8 + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + 3.0.0-alpha-13 + + + ${android.platform} + + true + + true + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.7 + + true + + + + + + + + org.apache.maven.plugins + maven-release-plugin + 2.2.1 + + true + + + + + org.apache.maven.plugins + maven-site-plugin + 2.1 + false + + false + false + ${basedir}/website + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + + org.apache.maven.wagon + wagon-ssh + 1.0-beta-7 + + + + + + + + org.apache.maven.scm + maven-scm-provider-gitexe + 1.4 + + + org.apache.maven.scm + maven-scm-manager-plexus + 1.4 + + + org.kathrynhuxtable.maven.wagon + wagon-gitsite + 0.3.1 + + + + diff --git a/actionbarsherlock/samples/README.md b/actionbarsherlock/samples/README.md new file mode 100644 index 000000000..2c461e84f --- /dev/null +++ b/actionbarsherlock/samples/README.md @@ -0,0 +1,13 @@ +ActionBarSherlock Samples +========================= + +See [actionbarsherlock.com/samples.html][1] for information on the samples +contained in this folder as well as a list of open source implementations. + + + + + + + + [1]: http://actionbarsherlock.com/samples.html diff --git a/actionbarsherlock/samples/demos/AndroidManifest.xml b/actionbarsherlock/samples/demos/AndroidManifest.xml new file mode 100644 index 000000000..29102b29a --- /dev/null +++ b/actionbarsherlock/samples/demos/AndroidManifest.xml @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/README.md b/actionbarsherlock/samples/demos/README.md new file mode 100644 index 000000000..a0198bf59 --- /dev/null +++ b/actionbarsherlock/samples/demos/README.md @@ -0,0 +1,13 @@ +ActionBarSherlock Sample: Demos +=============================== + +See [actionbarsherlock.com/samples.html][1] for information on the sample +contained in this folder. + + + + + + + + [1]: http://actionbarsherlock.com/samples.html diff --git a/actionbarsherlock/samples/demos/pom.xml b/actionbarsherlock/samples/demos/pom.xml new file mode 100644 index 000000000..4af0c4f30 --- /dev/null +++ b/actionbarsherlock/samples/demos/pom.xml @@ -0,0 +1,121 @@ + + + + 4.0.0 + + com.actionbarsherlock + sample-demos + Sample: Demos + apk + + + com.actionbarsherlock + parent-sample + 3.4.2 + ../pom.xml + + + + + android + android + provided + + + + com.google.android.maps + maps + provided + + + + com.actionbarsherlock + library + ${project.version} + apklib + + + + com.actionbarsherlock + plugin-maps + ${project.version} + + + + com.actionbarsherlock + plugin-preference + ${project.version} + + + + + src + ${apk.prefix}-${project.artifactId}-unaligned + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + + + + + release + + + performRelease + true + + + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + true + + + ${project.build.directory}/${project.build.finalName}.apk + ${project.build.directory}/${apk.prefix}-${project.artifactId}.apk + + + + + alignApk + package + + zipalign + + + + + + + com.jakewharton + github-deploy-maven-plugin + 1.0.1 + + + aligned + + + + deploy + + + + + + + diff --git a/actionbarsherlock/samples/demos/project.properties b/actionbarsherlock/samples/demos/project.properties new file mode 100644 index 000000000..34f549f8c --- /dev/null +++ b/actionbarsherlock/samples/demos/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. + +# Project target. +target=android-13 +android.library.reference.1=../../library diff --git a/actionbarsherlock/samples/demos/res/anim/decelerate_quint.xml b/actionbarsherlock/samples/demos/res/anim/decelerate_quint.xml new file mode 100644 index 000000000..ff2d5a9d0 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/anim/decelerate_quint.xml @@ -0,0 +1,21 @@ + + + + diff --git a/actionbarsherlock/samples/demos/res/anim/fragment_slide_left_enter.xml b/actionbarsherlock/samples/demos/res/anim/fragment_slide_left_enter.xml new file mode 100644 index 000000000..6bbf6a243 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/anim/fragment_slide_left_enter.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/anim/fragment_slide_left_exit.xml b/actionbarsherlock/samples/demos/res/anim/fragment_slide_left_exit.xml new file mode 100644 index 000000000..0affbc700 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/anim/fragment_slide_left_exit.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/anim/fragment_slide_right_enter.xml b/actionbarsherlock/samples/demos/res/anim/fragment_slide_right_enter.xml new file mode 100644 index 000000000..e2e116810 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/anim/fragment_slide_right_enter.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/anim/fragment_slide_right_exit.xml b/actionbarsherlock/samples/demos/res/anim/fragment_slide_right_exit.xml new file mode 100644 index 000000000..c8e6e7c8e --- /dev/null +++ b/actionbarsherlock/samples/demos/res/anim/fragment_slide_right_exit.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/drawable-hdpi/alert_dialog_icon.png b/actionbarsherlock/samples/demos/res/drawable-hdpi/alert_dialog_icon.png new file mode 100644 index 000000000..fe54477c9 Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-hdpi/alert_dialog_icon.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_compose.png b/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_compose.png new file mode 100644 index 000000000..cdb09e4c6 Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_compose.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_magnifying_glass.png b/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_magnifying_glass.png new file mode 100644 index 000000000..cdf35185e Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_magnifying_glass.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_refresh.png b/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_refresh.png new file mode 100644 index 000000000..08c32e09e Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_refresh.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_search.png b/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_search.png new file mode 100644 index 000000000..59de344e7 Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_search.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_title_share_default.png b/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_title_share_default.png new file mode 100644 index 000000000..ee4b285f7 Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-hdpi/ic_title_share_default.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-hdpi/icon.png b/actionbarsherlock/samples/demos/res/drawable-hdpi/icon.png new file mode 100644 index 000000000..723fbf062 Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-hdpi/icon.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-ldpi/icon.png b/actionbarsherlock/samples/demos/res/drawable-ldpi/icon.png new file mode 100644 index 000000000..181f679f5 Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-ldpi/icon.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-mdpi/alert_dialog_icon.png b/actionbarsherlock/samples/demos/res/drawable-mdpi/alert_dialog_icon.png new file mode 100644 index 000000000..0a7de0476 Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-mdpi/alert_dialog_icon.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-mdpi/ic_compose.png b/actionbarsherlock/samples/demos/res/drawable-mdpi/ic_compose.png new file mode 100644 index 000000000..15a413053 Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-mdpi/ic_compose.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-mdpi/ic_magnifying_glass.png b/actionbarsherlock/samples/demos/res/drawable-mdpi/ic_magnifying_glass.png new file mode 100644 index 000000000..53aa6d9fc Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-mdpi/ic_magnifying_glass.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-mdpi/ic_refresh.png b/actionbarsherlock/samples/demos/res/drawable-mdpi/ic_refresh.png new file mode 100644 index 000000000..55c43c327 Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-mdpi/ic_refresh.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-mdpi/ic_search.png b/actionbarsherlock/samples/demos/res/drawable-mdpi/ic_search.png new file mode 100644 index 000000000..75339d906 Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-mdpi/ic_search.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-mdpi/icon.png b/actionbarsherlock/samples/demos/res/drawable-mdpi/icon.png new file mode 100644 index 000000000..79f50d84c Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-mdpi/icon.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable-xhdpi/icon.png b/actionbarsherlock/samples/demos/res/drawable-xhdpi/icon.png new file mode 100644 index 000000000..34ca9a489 Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable-xhdpi/icon.png differ diff --git a/actionbarsherlock/samples/demos/res/drawable/custom_bg.xml b/actionbarsherlock/samples/demos/res/drawable/custom_bg.xml new file mode 100644 index 000000000..f70ec1b96 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/drawable/custom_bg.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/demos/res/drawable/custom_bg_img.png b/actionbarsherlock/samples/demos/res/drawable/custom_bg_img.png new file mode 100644 index 000000000..b314821f6 Binary files /dev/null and b/actionbarsherlock/samples/demos/res/drawable/custom_bg_img.png differ diff --git a/actionbarsherlock/samples/demos/res/layout-land/actionbar_feature_toggles_activity.xml b/actionbarsherlock/samples/demos/res/layout-land/actionbar_feature_toggles_activity.xml new file mode 100644 index 000000000..2b2e6e313 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout-land/actionbar_feature_toggles_activity.xml @@ -0,0 +1,383 @@ + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/fragment_dialog.xml b/actionbarsherlock/samples/demos/res/layout/fragment_dialog.xml new file mode 100644 index 000000000..f9dec5990 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/fragment_dialog.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/fragment_dialog_or_activity.xml b/actionbarsherlock/samples/demos/res/layout/fragment_dialog_or_activity.xml new file mode 100644 index 000000000..295f01787 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/fragment_dialog_or_activity.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/fragment_hide_show_support.xml b/actionbarsherlock/samples/demos/res/layout/fragment_hide_show_support.xml new file mode 100644 index 000000000..46950eb5b --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/fragment_hide_show_support.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/fragment_pager_list.xml b/actionbarsherlock/samples/demos/res/layout/fragment_pager_list.xml new file mode 100644 index 000000000..bbe7b1d64 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/fragment_pager_list.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/fragment_pagermenus.xml b/actionbarsherlock/samples/demos/res/layout/fragment_pagermenus.xml new file mode 100644 index 000000000..b429b545d --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/fragment_pagermenus.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/demos/res/layout/fragment_retain_instance.xml b/actionbarsherlock/samples/demos/res/layout/fragment_retain_instance.xml new file mode 100644 index 000000000..0dc39853f --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/fragment_retain_instance.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/fragment_stack.xml b/actionbarsherlock/samples/demos/res/layout/fragment_stack.xml new file mode 100644 index 000000000..1d12496ac --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/fragment_stack.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/fragment_tabs.xml b/actionbarsherlock/samples/demos/res/layout/fragment_tabs.xml new file mode 100644 index 000000000..0fb142cbc --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/fragment_tabs.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/fragment_tabs_pager.xml b/actionbarsherlock/samples/demos/res/layout/fragment_tabs_pager.xml new file mode 100644 index 000000000..c36cf3c30 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/fragment_tabs_pager.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/hello_world.xml b/actionbarsherlock/samples/demos/res/layout/hello_world.xml new file mode 100644 index 000000000..3d90a3351 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/hello_world.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/labeled_text_edit.xml b/actionbarsherlock/samples/demos/res/layout/labeled_text_edit.xml new file mode 100644 index 000000000..27568afbb --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/labeled_text_edit.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/list_item_icon_text.xml b/actionbarsherlock/samples/demos/res/layout/list_item_icon_text.xml new file mode 100644 index 000000000..c3825b719 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/list_item_icon_text.xml @@ -0,0 +1,32 @@ + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/local_service_broadcaster.xml b/actionbarsherlock/samples/demos/res/layout/local_service_broadcaster.xml new file mode 100644 index 000000000..f01120dce --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/local_service_broadcaster.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/receive_result.xml b/actionbarsherlock/samples/demos/res/layout/receive_result.xml new file mode 100644 index 000000000..5deb2ac4a --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/receive_result.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/send_result.xml b/actionbarsherlock/samples/demos/res/layout/send_result.xml new file mode 100644 index 000000000..bdd927c60 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/send_result.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/layout/simple_list_item_checkable_1.xml b/actionbarsherlock/samples/demos/res/layout/simple_list_item_checkable_1.xml new file mode 100644 index 000000000..f04db72ff --- /dev/null +++ b/actionbarsherlock/samples/demos/res/layout/simple_list_item_checkable_1.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/actionbarsherlock/samples/demos/res/values-v11/styles.xml b/actionbarsherlock/samples/demos/res/values-v11/styles.xml new file mode 100644 index 000000000..04c6f3f72 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/values-v11/styles.xml @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/values/arrays.xml b/actionbarsherlock/samples/demos/res/values/arrays.xml new file mode 100644 index 000000000..c82b34d34 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/values/arrays.xml @@ -0,0 +1,10 @@ + + + + Home + Email + Calendar + Browser + Clock + + diff --git a/actionbarsherlock/samples/demos/res/values/attrs.xml b/actionbarsherlock/samples/demos/res/values/attrs.xml new file mode 100644 index 000000000..0459aa5d2 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/values/attrs.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/res/values/colors.xml b/actionbarsherlock/samples/demos/res/values/colors.xml new file mode 100644 index 000000000..91061ea6f --- /dev/null +++ b/actionbarsherlock/samples/demos/res/values/colors.xml @@ -0,0 +1,23 @@ + + + + + #7f00 + #770000ff + #7700ff00 + #77ffff00 + #aa000000 + diff --git a/actionbarsherlock/samples/demos/res/values/strings.xml b/actionbarsherlock/samples/demos/res/values/strings.xml new file mode 100644 index 000000000..ae2bd72e4 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/values/strings.xml @@ -0,0 +1,139 @@ + + + + + ABS: Support Demos + + Hello, World! + Hello, ActionBar! + + + Lorem ipsum dolor sit aie consectetur adipiscing\nPlloaso mako nuto + siwuf cakso dodtos anr koop. + + OK + Cancel + + Initial text. + + Pick a result to send, or BACK to cancel. + Corky + Violet + Press the button to get an activity result, which will be displayed here: + Get Result + + + Action Bar/Feature Toggles + Action Bar/List Navigation + Action Bar/Tab Navigation + Action Bar/Tabs and Pager + Action Bar/Overlay + Action Bar/Menu Support + Action Bar/Indeterminate Progress + + Action Bar/Pager + Fragment Menus + Only the selected fragment contributes menu items to the action bar. + + Action Bar/Custom Navigation + If you find that list and tab navigation are not suitable for your application you may replace it with a custom layout. + + Action Bar/Action Item Text + Controlling the display of action item text on phones is as easy as requesting a window feature. See the source code for this activity for specific usage. + + Action Bar/Simple + This is a simple showcase of how easy it is to add the action bar to your activities. Take a look at the source code, it\'s surprisingly simple! + + Action Bar/Sub-menus + This activity is a demonstration of how sub-menus work when they are either an action item or an item in the overflow menu. + + Action Bar/Styles + Dark Theme + Light Theme + Custom Theme + + + Fragment/Alert Dialog + + Fragment/Arguments + Demonstrates a fragment that takes arguments + as a Bundle at runtime (on the right) or from attributes in a layout (on the left). + From Attributes + Landscape Only + + Fragment/Custom Animation + + Fragment/Hide and Show + + Fragment/Context Menu + Fragment populating a context + menu; long press the button to see. + Long press me + + Fragment/Dialog + Show + + Fragment/Dialog or Activity + Demonstrates the same fragment + being shown as a dialog and embedded inside of an activity. + Fragment embedded inside + of the activity: + + Fragment/Layout + + Fragment/List Array + + Fragment/Menu + Build menus from two fragments, allowing + you to hide them to remove them.. + Show fragment 1 menu + Show fragment 2 menu + + Fragment/Retain Instance + Current progress of retained fragment; + restarts if fragment is re-created. + Restart + + Fragment/Receive Result + + Fragment/Stack + New fragment + + Fragment/Tabs + + Fragment/Tabs and Pager + + Fragment/Pager + First + Last + + Fragment/Pager + Menus + Only the selected fragment contributes items to the options menu. + + Fragment/State Pager + + Loader/Cursor + + Loader/Custom + + Loader/Throttle + + Content/Local Service Broadcaster + + Demonstrates use of LocalBroadcastManager + to communicate from a Service to an Activity. + Start Service + Stop Service + diff --git a/actionbarsherlock/samples/demos/res/values/styles.xml b/actionbarsherlock/samples/demos/res/values/styles.xml new file mode 100644 index 000000000..7b0707287 --- /dev/null +++ b/actionbarsherlock/samples/demos/res/values/styles.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/Cheeses.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/Cheeses.java new file mode 100644 index 000000000..8ac4fece6 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/Cheeses.java @@ -0,0 +1,154 @@ +/* + * 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.sample.demos; + +public class Cheeses { + + public static final String[] sCheeseStrings = { + "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", + "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", + "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese", + "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell", + "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc", + "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss", + "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon", + "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase", + "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese", + "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy", + "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille", + "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore", + "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)", + "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves", + "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur", + "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon", + "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin", + "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)", + "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine", + "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza", + "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)", + "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta", + "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie", + "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat", + "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano", + "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain", + "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou", + "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar", + "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno", + "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack", + "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper", + "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)", + "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese", + "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza", + "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley", + "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino", + "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina", + "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby", + "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin", + "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester", + "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue", + "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz", + "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich", + "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue", + "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle", + "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia", + "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis", + "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus", + "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison", + "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois", + "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse", + "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese", + "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise", + "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra", + "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola", + "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost", + "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel", + "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve", + "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi", + "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti", + "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve", + "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster", + "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg", + "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa", + "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine", + "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese", + "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere", + "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire", + "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou", + "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger", + "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings", + "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse", + "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam", + "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego", + "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin", + "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)", + "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse", + "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda", + "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte", + "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio", + "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne", + "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)", + "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster", + "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel", + "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca", + "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre", + "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty", + "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela", + "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano", + "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage", + "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry", + "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid", + "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn", + "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse", + "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin", + "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin", + "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre", + "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone", + "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark", + "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit", + "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia", + "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)", + "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna", + "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera", + "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou", + "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder", + "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort", + "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr", + "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin", + "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre", + "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss", + "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela", + "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda", + "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain", + "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese", + "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale", + "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie", + "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri", + "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar", + "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance", + "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes", + "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet", + "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe", + "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa", + "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois", + "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue", + "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington", + "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou", + "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue", + "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano" + }; + +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/Shakespeare.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/Shakespeare.java new file mode 100644 index 000000000..8efa492bc --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/Shakespeare.java @@ -0,0 +1,223 @@ +package com.actionbarsherlock.sample.demos; + +public final class Shakespeare { + /** + * Our data, part 1. + */ + public static final String[] TITLES = + { + "Henry IV (1)", + "Henry V", + "Henry VIII", + "Richard II", + "Richard III", + "Merchant of Venice", + "Othello", + "King Lear" + }; + + /** + * Our data, part 2. + */ + public static final String[] DIALOGUE = + { + "So shaken as we are, so wan with care," + + "Find we a time for frighted peace to pant," + + "And breathe short-winded accents of new broils" + + "To be commenced in strands afar remote." + + "No more the thirsty entrance of this soil" + + "Shall daub her lips with her own children's blood;" + + "Nor more shall trenching war channel her fields," + + "Nor bruise her flowerets with the armed hoofs" + + "Of hostile paces: those opposed eyes," + + "Which, like the meteors of a troubled heaven," + + "All of one nature, of one substance bred," + + "Did lately meet in the intestine shock" + + "And furious close of civil butchery" + + "Shall now, in mutual well-beseeming ranks," + + "March all one way and be no more opposed" + + "Against acquaintance, kindred and allies:" + + "The edge of war, like an ill-sheathed knife," + + "No more shall cut his master. Therefore, friends," + + "As far as to the sepulchre of Christ," + + "Whose soldier now, under whose blessed cross" + + "We are impressed and engaged to fight," + + "Forthwith a power of English shall we levy;" + + "Whose arms were moulded in their mothers' womb" + + "To chase these pagans in those holy fields" + + "Over whose acres walk'd those blessed feet" + + "Which fourteen hundred years ago were nail'd" + + "For our advantage on the bitter cross." + + "But this our purpose now is twelve month old," + + "And bootless 'tis to tell you we will go:" + + "Therefore we meet not now. Then let me hear" + + "Of you, my gentle cousin Westmoreland," + + "What yesternight our council did decree" + + "In forwarding this dear expedience.", + + "Hear him but reason in divinity," + + "And all-admiring with an inward wish" + + "You would desire the king were made a prelate:" + + "Hear him debate of commonwealth affairs," + + "You would say it hath been all in all his study:" + + "List his discourse of war, and you shall hear" + + "A fearful battle render'd you in music:" + + "Turn him to any cause of policy," + + "The Gordian knot of it he will unloose," + + "Familiar as his garter: that, when he speaks," + + "The air, a charter'd libertine, is still," + + "And the mute wonder lurketh in men's ears," + + "To steal his sweet and honey'd sentences;" + + "So that the art and practic part of life" + + "Must be the mistress to this theoric:" + + "Which is a wonder how his grace should glean it," + + "Since his addiction was to courses vain," + + "His companies unletter'd, rude and shallow," + + "His hours fill'd up with riots, banquets, sports," + + "And never noted in him any study," + + "Any retirement, any sequestration" + + "From open haunts and popularity.", + + "I come no more to make you laugh: things now," + + "That bear a weighty and a serious brow," + + "Sad, high, and working, full of state and woe," + + "Such noble scenes as draw the eye to flow," + + "We now present. Those that can pity, here" + + "May, if they think it well, let fall a tear;" + + "The subject will deserve it. Such as give" + + "Their money out of hope they may believe," + + "May here find truth too. Those that come to see" + + "Only a show or two, and so agree" + + "The play may pass, if they be still and willing," + + "I'll undertake may see away their shilling" + + "Richly in two short hours. Only they" + + "That come to hear a merry bawdy play," + + "A noise of targets, or to see a fellow" + + "In a long motley coat guarded with yellow," + + "Will be deceived; for, gentle hearers, know," + + "To rank our chosen truth with such a show" + + "As fool and fight is, beside forfeiting" + + "Our own brains, and the opinion that we bring," + + "To make that only true we now intend," + + "Will leave us never an understanding friend." + + "Therefore, for goodness' sake, and as you are known" + + "The first and happiest hearers of the town," + + "Be sad, as we would make ye: think ye see" + + "The very persons of our noble story" + + "As they were living; think you see them great," + + "And follow'd with the general throng and sweat" + + "Of thousand friends; then in a moment, see" + + "How soon this mightiness meets misery:" + + "And, if you can be merry then, I'll say" + + "A man may weep upon his wedding-day.", + + "First, heaven be the record to my speech!" + + "In the devotion of a subject's love," + + "Tendering the precious safety of my prince," + + "And free from other misbegotten hate," + + "Come I appellant to this princely presence." + + "Now, Thomas Mowbray, do I turn to thee," + + "And mark my greeting well; for what I speak" + + "My body shall make good upon this earth," + + "Or my divine soul answer it in heaven." + + "Thou art a traitor and a miscreant," + + "Too good to be so and too bad to live," + + "Since the more fair and crystal is the sky," + + "The uglier seem the clouds that in it fly." + + "Once more, the more to aggravate the note," + + "With a foul traitor's name stuff I thy throat;" + + "And wish, so please my sovereign, ere I move," + + "What my tongue speaks my right drawn sword may prove.", + + "Now is the winter of our discontent" + + "Made glorious summer by this sun of York;" + + "And all the clouds that lour'd upon our house" + + "In the deep bosom of the ocean buried." + + "Now are our brows bound with victorious wreaths;" + + "Our bruised arms hung up for monuments;" + + "Our stern alarums changed to merry meetings," + + "Our dreadful marches to delightful measures." + + "Grim-visaged war hath smooth'd his wrinkled front;" + + "And now, instead of mounting barded steeds" + + "To fright the souls of fearful adversaries," + + "He capers nimbly in a lady's chamber" + + "To the lascivious pleasing of a lute." + + "But I, that am not shaped for sportive tricks," + + "Nor made to court an amorous looking-glass;" + + "I, that am rudely stamp'd, and want love's majesty" + + "To strut before a wanton ambling nymph;" + + "I, that am curtail'd of this fair proportion," + + "Cheated of feature by dissembling nature," + + "Deformed, unfinish'd, sent before my time" + + "Into this breathing world, scarce half made up," + + "And that so lamely and unfashionable" + + "That dogs bark at me as I halt by them;" + + "Why, I, in this weak piping time of peace," + + "Have no delight to pass away the time," + + "Unless to spy my shadow in the sun" + + "And descant on mine own deformity:" + + "And therefore, since I cannot prove a lover," + + "To entertain these fair well-spoken days," + + "I am determined to prove a villain" + + "And hate the idle pleasures of these days." + + "Plots have I laid, inductions dangerous," + + "By drunken prophecies, libels and dreams," + + "To set my brother Clarence and the king" + + "In deadly hate the one against the other:" + + "And if King Edward be as true and just" + + "As I am subtle, false and treacherous," + + "This day should Clarence closely be mew'd up," + + "About a prophecy, which says that 'G'" + + "Of Edward's heirs the murderer shall be." + + "Dive, thoughts, down to my soul: here" + + "Clarence comes.", + + "To bait fish withal: if it will feed nothing else," + + "it will feed my revenge. He hath disgraced me, and" + + "hindered me half a million; laughed at my losses," + + "mocked at my gains, scorned my nation, thwarted my" + + "bargains, cooled my friends, heated mine" + + "enemies; and what's his reason? I am a Jew. Hath" + + "not a Jew eyes? hath not a Jew hands, organs," + + "dimensions, senses, affections, passions? fed with" + + "the same food, hurt with the same weapons, subject" + + "to the same diseases, healed by the same means," + + "warmed and cooled by the same winter and summer, as" + + "a Christian is? If you prick us, do we not bleed?" + + "if you tickle us, do we not laugh? if you poison" + + "us, do we not die? and if you wrong us, shall we not" + + "revenge? If we are like you in the rest, we will" + + "resemble you in that. If a Jew wrong a Christian," + + "what is his humility? Revenge. If a Christian" + + "wrong a Jew, what should his sufferance be by" + + "Christian example? Why, revenge. The villany you" + + "teach me, I will execute, and it shall go hard but I" + + "will better the instruction.", + + "Virtue! a fig! 'tis in ourselves that we are thus" + + "or thus. Our bodies are our gardens, to the which" + + "our wills are gardeners: so that if we will plant" + + "nettles, or sow lettuce, set hyssop and weed up" + + "thyme, supply it with one gender of herbs, or" + + "distract it with many, either to have it sterile" + + "with idleness, or manured with industry, why, the" + + "power and corrigible authority of this lies in our" + + "wills. If the balance of our lives had not one" + + "scale of reason to poise another of sensuality, the" + + "blood and baseness of our natures would conduct us" + + "to most preposterous conclusions: but we have" + + "reason to cool our raging motions, our carnal" + + "stings, our unbitted lusts, whereof I take this that" + + "you call love to be a sect or scion.", + + "Blow, winds, and crack your cheeks! rage! blow!" + + "You cataracts and hurricanoes, spout" + + "Till you have drench'd our steeples, drown'd the cocks!" + + "You sulphurous and thought-executing fires," + + "Vaunt-couriers to oak-cleaving thunderbolts," + + "Singe my white head! And thou, all-shaking thunder," + + "Smite flat the thick rotundity o' the world!" + + "Crack nature's moulds, an germens spill at once," + + "That make ingrateful man!" + }; +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/Support4Demos.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/Support4Demos.java new file mode 100644 index 000000000..597ce3774 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/Support4Demos.java @@ -0,0 +1,149 @@ +/* + * 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.sample.demos; + +import android.app.ListActivity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.view.View; +import android.widget.ListView; +import android.widget.SimpleAdapter; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Support4Demos extends ListActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + String path = intent.getStringExtra("com.example.android.apis.Path"); + + if (path == null) { + path = ""; + } + + setListAdapter(new SimpleAdapter(this, getData(path), + android.R.layout.simple_list_item_1, new String[] { "title" }, + new int[] { android.R.id.text1 })); + getListView().setTextFilterEnabled(true); + } + + protected List> getData(String prefix) { + List> myData = new ArrayList>(); + + Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.addCategory("com.actionbarsherlock.sample.demos.SUPPORT4_SAMPLE_CODE"); + + PackageManager pm = getPackageManager(); + List list = pm.queryIntentActivities(mainIntent, 0); + + if (null == list) + return myData; + + String[] prefixPath; + String prefixWithSlash = prefix; + + if (prefix.equals("")) { + prefixPath = null; + } else { + prefixPath = prefix.split("/"); + prefixWithSlash = prefix + "/"; + } + + int len = list.size(); + + Map entries = new HashMap(); + + for (int i = 0; i < len; i++) { + ResolveInfo info = list.get(i); + CharSequence labelSeq = info.loadLabel(pm); + String label = labelSeq != null + ? labelSeq.toString() + : info.activityInfo.name; + + if (prefixWithSlash.length() == 0 || label.startsWith(prefixWithSlash)) { + + String[] labelPath = label.split("/"); + + String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length]; + + if ((prefixPath != null ? prefixPath.length : 0) == labelPath.length - 1) { + addItem(myData, nextLabel, activityIntent( + info.activityInfo.applicationInfo.packageName, + info.activityInfo.name)); + } else { + if (entries.get(nextLabel) == null) { + addItem(myData, nextLabel, browseIntent(prefix.equals("") ? nextLabel : prefix + "/" + nextLabel)); + entries.put(nextLabel, true); + } + } + } + } + + Collections.sort(myData, sDisplayNameComparator); + + return myData; + } + + private final static Comparator> sDisplayNameComparator = + new Comparator>() { + private final Collator collator = Collator.getInstance(); + + public int compare(Map map1, Map map2) { + return collator.compare(map1.get("title"), map2.get("title")); + } + }; + + protected Intent activityIntent(String pkg, String componentName) { + Intent result = new Intent(); + result.setClassName(pkg, componentName); + return result; + } + + protected Intent browseIntent(String path) { + Intent result = new Intent(); + result.setClass(this, Support4Demos.class); + result.putExtra("com.example.android.apis.Path", path); + return result; + } + + protected void addItem(List> data, String name, Intent intent) { + Map temp = new HashMap(); + temp.put("title", name); + temp.put("intent", intent); + data.add(temp); + } + + @Override + @SuppressWarnings("unchecked") + protected void onListItemClick(ListView l, View v, int position, long id) { + Map map = (Map)l.getItemAtPosition(position); + + Intent intent = (Intent) map.get("intent"); + startActivity(intent); + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarActionItemText.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarActionItemText.java new file mode 100644 index 000000000..69e635dc5 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarActionItemText.java @@ -0,0 +1,47 @@ +/* + * 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.sample.demos.app; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.support.v4.view.Window; +import android.widget.TextView; + +import com.actionbarsherlock.sample.demos.R; + +public class ActionBarActionItemText extends FragmentActivity { + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add("Text") + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + menu.add("Text") + .setIcon(R.drawable.ic_title_share_default) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + return super.onCreateOptionsMenu(menu); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_ACTION_BAR_ITEM_TEXT); + setContentView(R.layout.actionbar_text); + ((TextView)findViewById(R.id.text)).setText(R.string.actionbar_actionitemtext_content); + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarCustomNavigation.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarCustomNavigation.java new file mode 100644 index 000000000..32602aeab --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarCustomNavigation.java @@ -0,0 +1,50 @@ +/* + * 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.sample.demos.app; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.RadioGroup; +import android.widget.Toast; +import android.widget.RadioGroup.OnCheckedChangeListener; +import android.widget.TextView; +import com.actionbarsherlock.sample.demos.R; + +public class ActionBarCustomNavigation extends FragmentActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.actionbar_text); + ((TextView)findViewById(R.id.text)).setText(R.string.actionbar_customnavigation_content); + + //Inflate the custom view + View customNav = LayoutInflater.from(this).inflate(R.layout.actionbar_custom_view, null); + + //Bind to its state change + ((RadioGroup)customNav.findViewById(R.id.radio_nav)).setOnCheckedChangeListener(new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + Toast.makeText(ActionBarCustomNavigation.this, "Navigation selection changed.", Toast.LENGTH_SHORT).show(); + } + }); + + //Attach to the action bar + getSupportActionBar().setCustomView(customNav); + getSupportActionBar().setDisplayShowCustomEnabled(true); + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarFeatureToggles.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarFeatureToggles.java new file mode 100644 index 000000000..74ed640b2 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarFeatureToggles.java @@ -0,0 +1,254 @@ +package com.actionbarsherlock.sample.demos.app; + +import java.util.Random; +import com.actionbarsherlock.sample.demos.R; +import android.os.Bundle; +import android.support.v4.app.ActionBar; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.support.v4.view.Window; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.Button; + +public class ActionBarFeatureToggles extends FragmentActivity { + private static final Random RANDOM = new Random(); + + private int items = 0; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + for (int i = 0; i < items; i++) { + menu.add("Text") + .setIcon(R.drawable.ic_title_share_default) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + + return super.onCreateOptionsMenu(menu); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + requestWindowFeature(Window.FEATURE_PROGRESS); + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + super.onCreate(savedInstanceState); + + setContentView(R.layout.actionbar_feature_toggles_activity); + setProgressBarIndeterminateVisibility(Boolean.FALSE); + setProgressBarVisibility(false); + + getSupportActionBar().setCustomView(R.layout.actionbar_custom_view); + getSupportActionBar().setDisplayShowCustomEnabled(false); + getSupportActionBar().setDisplayShowHomeEnabled(false); + + ArrayAdapter listAdapter = ArrayAdapter.createFromResource(this, R.array.locations, R.layout.abs__simple_spinner_item); + listAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + getSupportActionBar().setListNavigationCallbacks(listAdapter, null); + + findViewById(R.id.display_progress_show).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setProgressBarVisibility(true); + setProgressBarIndeterminateVisibility(Boolean.FALSE); + setProgress(RANDOM.nextInt(8000) + 10); + } + }); + findViewById(R.id.display_progress_hide).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setProgressBarVisibility(false); + } + }); + findViewById(R.id.display_iprogress_show).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setProgressBarIndeterminateVisibility(Boolean.TRUE); + //Hack to hide the regular progress bar + setProgress(0); + } + }); + findViewById(R.id.display_iprogress_hide).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setProgressBarIndeterminateVisibility(Boolean.FALSE); + } + }); + + findViewById(R.id.display_items_clear).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + items = 0; + invalidateOptionsMenu(); + } + }); + findViewById(R.id.display_items_add).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + items += 1; + invalidateOptionsMenu(); + } + }); + + findViewById(R.id.display_subtitle_show).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setSubtitle("The quick brown fox jumps over the lazy dog."); + } + }); + findViewById(R.id.display_subtitle_hide).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setSubtitle(null); + } + }); + + findViewById(R.id.display_title_show).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setDisplayShowTitleEnabled(true); + } + }); + findViewById(R.id.display_title_hide).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setDisplayShowTitleEnabled(false); + } + }); + + findViewById(R.id.display_custom_show).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setDisplayShowCustomEnabled(true); + } + }); + findViewById(R.id.display_custom_hide).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setDisplayShowCustomEnabled(false); + } + }); + + findViewById(R.id.navigation_standard).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); + } + }); + findViewById(R.id.navigation_list).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); + } + }); + findViewById(R.id.navigation_tabs).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + } + }); + + findViewById(R.id.display_home_as_up_show).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + }); + findViewById(R.id.display_home_as_up_hide).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setDisplayHomeAsUpEnabled(false); + } + }); + + findViewById(R.id.display_logo_show).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setDisplayUseLogoEnabled(true); + } + }); + findViewById(R.id.display_logo_hide).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setDisplayUseLogoEnabled(false); + } + }); + + findViewById(R.id.display_home_show).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setDisplayShowHomeEnabled(true); + } + }); + findViewById(R.id.display_home_hide).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().setDisplayShowHomeEnabled(false); + } + }); + + findViewById(R.id.display_actionbar_show).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().show(); + } + }); + findViewById(R.id.display_actionbar_hide).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().hide(); + } + }); + + Button tabAdd = (Button)findViewById(R.id.display_tab_add); + tabAdd.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + ActionBar.Tab newTab = getSupportActionBar().newTab(); + + if (RANDOM.nextBoolean()) { + newTab.setCustomView(R.layout.actionbar_tab_custom_view); + } else { + boolean icon = RANDOM.nextBoolean(); + if (icon) { + newTab.setIcon(R.drawable.ic_title_share_default); + } + if (!icon || RANDOM.nextBoolean()) { + newTab.setText("Text!"); + } + } + + getSupportActionBar().addTab(newTab); + } + }); + //Add some tabs + tabAdd.performClick(); + tabAdd.performClick(); + tabAdd.performClick(); + + findViewById(R.id.display_tab_select).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (getSupportActionBar().getTabCount() > 0) { + getSupportActionBar().selectTab( + getSupportActionBar().getTabAt( + RANDOM.nextInt(getSupportActionBar().getTabCount()) + ) + ); + } + } + }); + findViewById(R.id.display_tab_remove).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().removeTabAt(getSupportActionBar().getTabCount() - 1); + } + }); + findViewById(R.id.display_tab_remove_all).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getSupportActionBar().removeAllTabs(); + } + }); + } +} \ No newline at end of file diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarIndeterminateProgress.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarIndeterminateProgress.java new file mode 100644 index 000000000..f4e288c2d --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarIndeterminateProgress.java @@ -0,0 +1,53 @@ +/* + * 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.sample.demos.app; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Window; +import android.view.View; +import com.actionbarsherlock.sample.demos.R; + +public class ActionBarIndeterminateProgress extends FragmentActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //This has to be called before setContentView and you must use the + //class in android.support.v4.view and NOT android.view + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + + setContentView(R.layout.actionbar_iprogress); + setProgressBarIndeterminateVisibility(Boolean.FALSE); + + + //Bind to the buttons which enable and disable the progress spinner. + //Notice how we *MUST* pass TRUE/FALSE objects rather than the native + //true/false values. + findViewById(R.id.enable).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View arg0) { + setProgressBarIndeterminateVisibility(Boolean.TRUE); + } + }); + findViewById(R.id.disable).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View arg0) { + setProgressBarIndeterminateVisibility(Boolean.FALSE); + } + }); + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarListNavigation.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarListNavigation.java new file mode 100644 index 000000000..74caf8cd1 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarListNavigation.java @@ -0,0 +1,37 @@ +package com.actionbarsherlock.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; +import android.os.Bundle; +import android.support.v4.app.ActionBar; +import android.support.v4.app.FragmentActivity; +import android.widget.ArrayAdapter; + +public class ActionBarListNavigation extends FragmentActivity implements ActionBar.OnNavigationListener { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //WARNING: This should normally not be needed as calling setContentView + //or attaching a fragment to android.R.id.content will call this. In + //this case, however, we call it manually since initializing the list + //navigation will trigger a navigation changed callback and thus attach + //the default fragment as the content. + ensureSupportActionBarAttached(); + + + getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); + + ArrayAdapter list = ArrayAdapter.createFromResource(this, R.array.locations, R.layout.abs__simple_spinner_item); + list.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + getSupportActionBar().setListNavigationCallbacks(list, this); + } + + @Override + public boolean onNavigationItemSelected(int itemPosition, long itemId) { + getSupportFragmentManager() + .beginTransaction() + .replace(android.R.id.content, FragmentStackSupport.CountingFragment.newInstance(itemPosition)) + .commit(); + return true; + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarMenuSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarMenuSupport.java new file mode 100644 index 000000000..15e9ae76f --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarMenuSupport.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2011 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; + +import android.os.Bundle; +import android.view.MenuInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.CheckBox; + +/** + * Demonstrates how fragments can participate in the options menu. + */ +public class ActionBarMenuSupport extends FragmentActivity { + Fragment mFragment1; + Fragment mFragment2; + CheckBox mCheckBox1; + CheckBox mCheckBox2; + + // Update fragment visibility when check boxes are changed. + final OnClickListener mClickListener = new OnClickListener() { + public void onClick(View v) { + updateFragmentVisibility(); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_menu); + + // Make sure the two menu fragments are created. + FragmentManager fm = getSupportFragmentManager(); + FragmentTransaction ft = fm.beginTransaction(); + mFragment1 = fm.findFragmentByTag("f1"); + if (mFragment1 == null) { + mFragment1 = new MenuFragment(); + ft.add(mFragment1, "f1"); + } + mFragment2 = fm.findFragmentByTag("f2"); + if (mFragment2 == null) { + mFragment2 = new Menu2Fragment(); + ft.add(mFragment2, "f2"); + } + ft.commit(); + + // Watch check box clicks. + mCheckBox1 = (CheckBox)findViewById(R.id.menu1); + mCheckBox1.setOnClickListener(mClickListener); + mCheckBox2 = (CheckBox)findViewById(R.id.menu2); + mCheckBox2.setOnClickListener(mClickListener); + + // Make sure fragments start out with correct visibility. + updateFragmentVisibility(); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + // Make sure fragments are updated after check box view state is restored. + updateFragmentVisibility(); + } + + // Update fragment visibility based on current check box state. + void updateFragmentVisibility() { + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + if (mCheckBox1.isChecked()) ft.show(mFragment1); + else ft.hide(mFragment1); + if (mCheckBox2.isChecked()) ft.show(mFragment2); + else ft.hide(mFragment2); + ft.commit(); + } + + /** + * A fragment that displays a menu. This fragment happens to not + * have a UI (it does not implement onCreateView), but it could also + * have one if it wanted. + */ + public static class MenuFragment extends Fragment { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add("Menu 1a") + .setIcon(R.drawable.ic_title_share_default) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + + menu.add("Menu 1b") + .setIcon(R.drawable.ic_title_share_default) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + } + } + + /** + * Second fragment with a menu. + */ + public static class Menu2Fragment extends Fragment { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add("Menu 2") + .setIcon(R.drawable.ic_title_share_default) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarOverlay.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarOverlay.java new file mode 100644 index 000000000..250f95bf0 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarOverlay.java @@ -0,0 +1,45 @@ +/* + * 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.sample.demos.app; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Window; +import android.widget.TextView; +import com.actionbarsherlock.sample.demos.Shakespeare; +import com.actionbarsherlock.sample.demos.R; + +public class ActionBarOverlay extends FragmentActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); + super.onCreate(savedInstanceState); + setContentView(R.layout.actionbar_overlay); + + //Load partially transparent black background + getSupportActionBar().setBackgroundDrawable(getResources().getDrawable(R.drawable.ab_bg_black)); + + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 3; i++) { + for (String dialog : Shakespeare.DIALOGUE) { + builder.append(dialog).append("\n\n"); + } + } + + TextView bunchOfText = (TextView)findViewById(R.id.bunch_of_text); + bunchOfText.setText(builder.toString()); + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarPagerFragmentMenus.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarPagerFragmentMenus.java new file mode 100644 index 000000000..a8376e1d1 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarPagerFragmentMenus.java @@ -0,0 +1,98 @@ +package com.actionbarsherlock.sample.demos.app; + +import java.util.Random; +import com.actionbarsherlock.sample.demos.R; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.support.v4.view.ViewPager; +import android.support.v4.view.Window; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class ActionBarPagerFragmentMenus extends FragmentActivity { + private static final Random RANDOM = new Random(); + private static final int PAGES = 10; + private static final int MENU_ITEM_RANDOM = 1; + + private ViewPager mPager; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_ACTION_BAR_ITEM_TEXT); + + setContentView(R.layout.actionbar_pagerfragmentmenus); + mPager = (ViewPager)findViewById(R.id.pager); + mPager.setAdapter(new TestAdapter(getSupportFragmentManager())); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, MENU_ITEM_RANDOM, 0, "Random"); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == MENU_ITEM_RANDOM) { + mPager.setCurrentItem(RANDOM.nextInt(PAGES)); + return true; + } + return super.onOptionsItemSelected(item); + } + + static final class TestAdapter extends FragmentPagerAdapter { + public TestAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public int getCount() { + return PAGES; + } + + @Override + public Fragment getItem(int position) { + TestFragment f = new TestFragment(); + f.text = String.valueOf(position + 1); + return f; + } + } + + public static class TestFragment extends Fragment { + String text = "???"; + + public TestFragment() { + setRetainInstance(true); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + TextView tv = new TextView(getActivity()); + tv.setGravity(Gravity.CENTER); + tv.setTextSize(50); + tv.setText(text); + return tv; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add(text).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarSimple.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarSimple.java new file mode 100644 index 000000000..7baafe13d --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarSimple.java @@ -0,0 +1,30 @@ +/* + * 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.sample.demos.app; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.widget.TextView; +import com.actionbarsherlock.sample.demos.R; + +public class ActionBarSimple extends FragmentActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.actionbar_text); + ((TextView)findViewById(R.id.text)).setText(R.string.actionbar_simple_content); + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarStyles.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarStyles.java new file mode 100644 index 000000000..e744d8a94 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarStyles.java @@ -0,0 +1,60 @@ +package com.actionbarsherlock.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; + +public class ActionBarStyles extends FragmentActivity { + private static int THEME = R.style.Theme_Sherlock; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add("Save") + .setIcon(R.drawable.ic_compose) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + menu.add("Search") + .setIcon(R.drawable.ic_search) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + menu.add("Refresh") + .setIcon(R.drawable.ic_refresh) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + return super.onCreateOptionsMenu(menu); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + setTheme(THEME); + super.onCreate(savedInstanceState); + setContentView(R.layout.actionbar_styles); + + ((Button)findViewById(R.id.theme_dark)).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + THEME = R.style.Theme_Sherlock; + recreate(); + } + }); + ((Button)findViewById(R.id.theme_light)).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + THEME = R.style.Theme_Sherlock_Light; + recreate(); + } + }); + ((Button)findViewById(R.id.theme_custom)).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + THEME = R.style.Theme_SherlockCustom; + recreate(); + } + }); + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarSubMenus.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarSubMenus.java new file mode 100644 index 000000000..5f5e19c6f --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarSubMenus.java @@ -0,0 +1,58 @@ +/* + * 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.sample.demos.app; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.support.v4.view.SubMenu; +import android.widget.TextView; + +import com.actionbarsherlock.sample.demos.R; + +public class ActionBarSubMenus extends FragmentActivity { + @Override + public boolean onCreateOptionsMenu(Menu menu) { + + SubMenu subMenu1 = menu.addSubMenu("Action Item"); + subMenu1.add("Sample"); + subMenu1.add("Menu"); + subMenu1.add("Items"); + + MenuItem subMenu1Item = subMenu1.getItem(); + subMenu1Item.setIcon(R.drawable.ic_title_share_default); + subMenu1Item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + SubMenu subMenu2 = menu.addSubMenu("Overflow Item"); + subMenu2.add("These"); + subMenu2.add("Are"); + subMenu2.add("Sample"); + subMenu2.add("Items"); + + MenuItem subMenu2Item = subMenu2.getItem(); + subMenu2Item.setIcon(R.drawable.ic_compose); + + return super.onCreateOptionsMenu(menu); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.actionbar_text); + ((TextView)findViewById(R.id.text)).setText(R.string.actionbar_submenus_content); + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarTabNavigation.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarTabNavigation.java new file mode 100644 index 000000000..18d99661e --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarTabNavigation.java @@ -0,0 +1,43 @@ +package com.actionbarsherlock.sample.demos.app; + +import android.os.Bundle; +import android.support.v4.app.ActionBar; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.app.ActionBar.Tab; + +public class ActionBarTabNavigation extends FragmentActivity implements ActionBar.TabListener { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getSupportFragmentManager() + .beginTransaction() + .add(android.R.id.content, FragmentStackSupport.CountingFragment.newInstance(0)) + .commit(); + + getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + for (int i = 0; i < 3; i++) { + ActionBar.Tab tab = getSupportActionBar().newTab(); + tab.setText("Tab " + i); + tab.setTabListener(this); + getSupportActionBar().addTab(tab); + } + } + + @Override + public void onTabReselected(Tab tab, FragmentTransaction ft) { + } + + @Override + public void onTabSelected(Tab tab, FragmentTransaction ft) { + getSupportFragmentManager() + .beginTransaction() + .replace(android.R.id.content, FragmentStackSupport.CountingFragment.newInstance(tab.getPosition())) + .commit(); + } + + @Override + public void onTabUnselected(Tab tab, FragmentTransaction ft) { + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarTabsPager.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarTabsPager.java new file mode 100644 index 000000000..caf0dd546 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/ActionBarTabsPager.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2011 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.sample.demos.app; + +import java.util.ArrayList; +import com.actionbarsherlock.sample.demos.R; +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.ActionBar; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.app.ActionBar.Tab; +import android.support.v4.view.ViewPager; + +/** + * Demonstrates combining the action bar with a ViewPager to implement a tab UI + * that switches between tabs and also allows the user to perform horizontal + * flicks to move between the tabs. + */ +public class ActionBarTabsPager extends FragmentActivity { + ViewPager mViewPager; + TabsAdapter mTabsAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.actionbar_tabs_pager); + getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + + ActionBar.Tab tab1 = getSupportActionBar().newTab().setText("Tab 1"); + ActionBar.Tab tab2 = getSupportActionBar().newTab().setText("Tab 2"); + ActionBar.Tab tab3 = getSupportActionBar().newTab().setText("Tab 3"); + ActionBar.Tab tab4 = getSupportActionBar().newTab().setText("Tab 4"); + + mViewPager = (ViewPager)findViewById(R.id.pager); + mTabsAdapter = new TabsAdapter(this, getSupportActionBar(), mViewPager); + mTabsAdapter.addTab(tab1, FragmentStackSupport.CountingFragment.class); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR) { + mTabsAdapter.addTab(tab2, FragmentStackSupport.CountingFragment.class); + mTabsAdapter.addTab(tab3, FragmentStackSupport.CountingFragment.class); + mTabsAdapter.addTab(tab4, FragmentStackSupport.CountingFragment.class); + } else { + mTabsAdapter.addTab(tab2, LoaderCursorSupport.CursorLoaderListFragment.class); + mTabsAdapter.addTab(tab3, LoaderCustomSupport.AppListFragment.class); + mTabsAdapter.addTab(tab4, LoaderThrottleSupport.ThrottledLoaderListFragment.class); + } + + if (savedInstanceState != null) { + getSupportActionBar().setSelectedNavigationItem(savedInstanceState.getInt("index")); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt("index", getSupportActionBar().getSelectedNavigationIndex()); + } + + /** + * This is a helper class that implements the management of tabs and all + * details of connecting a ViewPager with associated TabHost. It relies on a + * trick. Normally a tab host has a simple API for supplying a View or + * Intent that each tab will show. This is not sufficient for switching + * between pages. So instead we make the content part of the tab host + * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy + * view to show as the tab content. It listens to changes in tabs, and takes + * care of switch to the correct paged in the ViewPager whenever the selected + * tab changes. + */ + public static class TabsAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener, ActionBar.TabListener { + private final Context mContext; + private final ActionBar mActionBar; + private final ViewPager mViewPager; + private final ArrayList mTabs = new ArrayList(); + + public TabsAdapter(FragmentActivity activity, ActionBar actionBar, ViewPager pager) { + super(activity.getSupportFragmentManager()); + mContext = activity; + mActionBar = actionBar; + mViewPager = pager; + mViewPager.setAdapter(this); + mViewPager.setOnPageChangeListener(this); + } + + public void addTab(ActionBar.Tab tab, Class clss) { + mTabs.add(clss.getName()); + mActionBar.addTab(tab.setTabListener(this)); + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mTabs.size(); + } + + @Override + public Fragment getItem(int position) { + return Fragment.instantiate(mContext, mTabs.get(position), null); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + mActionBar.setSelectedNavigationItem(position); + } + + @Override + public void onPageScrollStateChanged(int state) { + } + + @Override + public void onTabSelected(Tab tab, FragmentTransaction ft) { + mViewPager.setCurrentItem(tab.getPosition()); + } + + @Override + public void onTabReselected(Tab tab, FragmentTransaction ft) { + } + + @Override + public void onTabUnselected(Tab tab, FragmentTransaction ft) { + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentAlertDialogSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentAlertDialogSupport.java new file mode 100644 index 000000000..bbf410782 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentAlertDialogSupport.java @@ -0,0 +1,110 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; + +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.TextView; + +/** + * Demonstrates how to show an AlertDialog that is managed by a Fragment. + */ +public class FragmentAlertDialogSupport extends FragmentActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_dialog); + + View tv = findViewById(R.id.text); + ((TextView)tv).setText("Example of displaying an alert dialog with a DialogFragment"); + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.show); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + showDialog(); + } + }); + } + + + void showDialog() { + DialogFragment newFragment = MyAlertDialogFragment.newInstance( + R.string.alert_dialog_two_buttons_title); + newFragment.show(getSupportFragmentManager(), "dialog"); + } + + public void doPositiveClick() { + // Do stuff here. + Log.i("FragmentAlertDialog", "Positive click!"); + } + + public void doNegativeClick() { + // Do stuff here. + Log.i("FragmentAlertDialog", "Negative click!"); + } + + + + public static class MyAlertDialogFragment extends DialogFragment { + + public static MyAlertDialogFragment newInstance(int title) { + MyAlertDialogFragment frag = new MyAlertDialogFragment(); + Bundle args = new Bundle(); + args.putInt("title", title); + frag.setArguments(args); + return frag; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + int title = getArguments().getInt("title"); + + return new AlertDialog.Builder(getActivity()) + .setIcon(R.drawable.alert_dialog_icon) + .setTitle(title) + .setPositiveButton(R.string.alert_dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + ((FragmentAlertDialogSupport)getActivity()).doPositiveClick(); + } + } + ) + .setNegativeButton(R.string.alert_dialog_cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + ((FragmentAlertDialogSupport)getActivity()).doNegativeClick(); + } + } + ) + .create(); + } + } + +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentArgumentsSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentArgumentsSupport.java new file mode 100644 index 000000000..2d6f67ee5 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentArgumentsSupport.java @@ -0,0 +1,111 @@ +/* + * 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.sample.demos.app; + +import android.content.res.TypedArray; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.app.SupportActivity; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import com.actionbarsherlock.sample.demos.R; + +/** + * Demonstrates a fragment that can be configured through both Bundle arguments + * and layout attributes. + */ +public class FragmentArgumentsSupport extends FragmentActivity { + + @Override protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_arguments_support); + + if (savedInstanceState == null) { + // First-time init; create fragment to embed in activity. + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + Fragment newFragment = MyFragment.newInstance("From Arguments"); + ft.add(R.id.created, newFragment); + ft.commit(); + } + } + + + + public static class MyFragment extends Fragment { + CharSequence mLabel; + + /** + * Create a new instance of MyFragment that will be initialized + * with the given arguments. + */ + static MyFragment newInstance(CharSequence label) { + MyFragment f = new MyFragment(); + Bundle b = new Bundle(); + b.putCharSequence("label", label); + f.setArguments(b); + return f; + } + + /** + * Parse attributes during inflation from a view hierarchy into the + * arguments we handle. + */ + @Override public void onInflate(SupportActivity activity, AttributeSet attrs, + Bundle savedInstanceState) { + super.onInflate(activity, attrs, savedInstanceState); + + TypedArray a = activity.obtainStyledAttributes(attrs, + R.styleable.FragmentArguments); + mLabel = a.getText(R.styleable.FragmentArguments_android_label); + a.recycle(); + } + + /** + * During creation, if arguments have been supplied to the fragment + * then parse those out. + */ + @Override public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle args = getArguments(); + if (args != null) { + CharSequence label = args.getCharSequence("label"); + if (label != null) { + mLabel = label; + } + } + } + + /** + * Create the view for this fragment, using the arguments given to it. + */ + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.hello_world, container, false); + View tv = v.findViewById(R.id.text); + ((TextView)tv).setText(mLabel != null ? mLabel : "(no label)"); + tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb)); + return v; + } + } + +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentContextMenuSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentContextMenuSupport.java new file mode 100644 index 000000000..5519c7616 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentContextMenuSupport.java @@ -0,0 +1,78 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.util.Log; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +/** + * Demonstration of displaying a context menu from a fragment. + */ +public class FragmentContextMenuSupport extends FragmentActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Create the list fragment and add it as our sole content. + ContextMenuFragment content = new ContextMenuFragment(); + getSupportFragmentManager().beginTransaction().add( + android.R.id.content, content).commit(); + } + + public static class ContextMenuFragment extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.fragment_context_menu, container, false); + registerForContextMenu(root.findViewById(R.id.long_press)); + return root; + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + menu.add(Menu.NONE, 1, Menu.NONE, "Menu A"); + menu.add(Menu.NONE, 2, Menu.NONE, "Menu B"); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + switch (item.getItemId()) { + case 1: + Log.i("ContextMenu", "Item 1a was chosen"); + return true; + case 2: + Log.i("ContextMenu", "Item 1b was chosen"); + return true; + } + return super.onContextItemSelected(item); + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentCustomAnimationSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentCustomAnimationSupport.java new file mode 100644 index 000000000..aa6941b08 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentCustomAnimationSupport.java @@ -0,0 +1,128 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.TextView; + +public class FragmentCustomAnimationSupport extends FragmentActivity { + int mStackLevel = 1; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_stack); + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.new_fragment); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + addFragmentToStack(); + } + }); + + if (savedInstanceState == null) { + // Do first time initialization -- add initial fragment. + Fragment newFragment = CountingFragment.newInstance(mStackLevel); + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.add(R.id.simple_fragment, newFragment).commit(); + } else { + mStackLevel = savedInstanceState.getInt("level"); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt("level", mStackLevel); + } + + + void addFragmentToStack() { + mStackLevel++; + + // Instantiate a new fragment. + Fragment newFragment = CountingFragment.newInstance(mStackLevel); + + // Add the fragment to the activity, pushing this transaction + // on to the back stack. + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.setCustomAnimations(R.anim.fragment_slide_left_enter, + R.anim.fragment_slide_left_exit, + R.anim.fragment_slide_right_enter, + R.anim.fragment_slide_right_exit); + ft.replace(R.id.simple_fragment, newFragment); + ft.addToBackStack(null); + ft.commit(); + } + + + + public static class CountingFragment extends Fragment { + int mNum; + + /** + * Create a new instance of CountingFragment, providing "num" + * as an argument. + */ + static CountingFragment newInstance(int num) { + CountingFragment f = new CountingFragment(); + + // Supply num input as an argument. + Bundle args = new Bundle(); + args.putInt("num", num); + f.setArguments(args); + + return f; + } + + /** + * When creating, retrieve this instance's number from its arguments. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mNum = getArguments() != null ? getArguments().getInt("num") : 1; + } + + /** + * The Fragment's UI is just a simple text view showing its + * instance number. + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.hello_world, container, false); + View tv = v.findViewById(R.id.text); + ((TextView)tv).setText("Fragment #" + mNum); + tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb)); + return v; + } + } + +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentDialogOrActivitySupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentDialogOrActivitySupport.java new file mode 100644 index 000000000..57e977e67 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentDialogOrActivitySupport.java @@ -0,0 +1,81 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; + +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +public class FragmentDialogOrActivitySupport extends FragmentActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_dialog_or_activity); + + if (savedInstanceState == null) { + // First-time init; create fragment to embed in activity. + + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + DialogFragment newFragment = MyDialogFragment.newInstance(); + ft.add(R.id.embedded, newFragment); + ft.commit(); + + } + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.show_dialog); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + showDialog(); + } + }); + } + + + void showDialog() { + // Create the fragment and show it as a dialog. + DialogFragment newFragment = MyDialogFragment.newInstance(); + newFragment.show(getSupportFragmentManager(), "dialog"); + } + + + + public static class MyDialogFragment extends DialogFragment { + static MyDialogFragment newInstance() { + return new MyDialogFragment(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.hello_world, container, false); + View tv = v.findViewById(R.id.text); + ((TextView)tv).setText("This is an instance of MyDialogFragment"); + return v; + } + } + +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentDialogSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentDialogSupport.java new file mode 100644 index 000000000..fbabe2751 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentDialogSupport.java @@ -0,0 +1,168 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; + +import android.support.v4.app.DialogFragment; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +public class FragmentDialogSupport extends FragmentActivity { + int mStackLevel = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_dialog); + + View tv = findViewById(R.id.text); + ((TextView)tv).setText("Example of displaying dialogs with a DialogFragment. " + + "Press the show button below to see the first dialog; pressing " + + "successive show buttons will display other dialog styles as a " + + "stack, with dismissing or back going to the previous dialog."); + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.show); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + showDialog(); + } + }); + + if (savedInstanceState != null) { + mStackLevel = savedInstanceState.getInt("level"); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt("level", mStackLevel); + } + + + void showDialog() { + mStackLevel++; + + // DialogFragment.show() will take care of adding the fragment + // in a transaction. We also want to remove any currently showing + // dialog, so make our own transaction and take care of that here. + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog"); + if (prev != null) { + ft.remove(prev); + } + ft.addToBackStack(null); + + // Create and show the dialog. + DialogFragment newFragment = MyDialogFragment.newInstance(mStackLevel); + newFragment.show(ft, "dialog"); + } + + + static String getNameForNum(int num) { + switch ((num-1)%6) { + case 1: return "STYLE_NO_TITLE"; + case 2: return "STYLE_NO_FRAME"; + case 3: return "STYLE_NO_INPUT (this window can't receive input, so " + + "you will need to press the bottom show button)"; + case 4: return "STYLE_NORMAL with dark fullscreen theme"; + case 5: return "STYLE_NORMAL with light theme"; + case 6: return "STYLE_NO_TITLE with light theme"; + case 7: return "STYLE_NO_FRAME with light theme"; + case 8: return "STYLE_NORMAL with light fullscreen theme"; + } + return "STYLE_NORMAL"; + } + + + public static class MyDialogFragment extends DialogFragment { + int mNum; + + /** + * Create a new instance of MyDialogFragment, providing "num" + * as an argument. + */ + static MyDialogFragment newInstance(int num) { + MyDialogFragment f = new MyDialogFragment(); + + // Supply num input as an argument. + Bundle args = new Bundle(); + args.putInt("num", num); + f.setArguments(args); + + return f; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mNum = getArguments().getInt("num"); + + // Pick a style based on the num. + int style = DialogFragment.STYLE_NORMAL, theme = 0; + switch ((mNum-1)%6) { + case 1: style = DialogFragment.STYLE_NO_TITLE; break; + case 2: style = DialogFragment.STYLE_NO_FRAME; break; + case 3: style = DialogFragment.STYLE_NO_INPUT; break; + case 4: style = DialogFragment.STYLE_NORMAL; break; + case 5: style = DialogFragment.STYLE_NO_TITLE; break; + case 6: style = DialogFragment.STYLE_NO_FRAME; break; + case 7: style = DialogFragment.STYLE_NORMAL; break; + } + switch ((mNum-1)%6) { + case 2: theme = android.R.style.Theme_Panel; break; + case 4: theme = android.R.style.Theme; break; + case 5: theme = android.R.style.Theme_Light; break; + case 6: theme = android.R.style.Theme_Light_Panel; break; + case 7: theme = android.R.style.Theme_Light; break; + } + setStyle(style, theme); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_dialog, container, false); + View tv = v.findViewById(R.id.text); + ((TextView)tv).setText("Dialog #" + mNum + ": using style " + + getNameForNum(mNum)); + + // Watch for button clicks. + Button button = (Button)v.findViewById(R.id.show); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + // When button is clicked, call up to owning activity. + ((FragmentDialogSupport)getActivity()).showDialog(); + } + }); + + return v; + } + } + +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentHideShowSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentHideShowSupport.java new file mode 100644 index 000000000..c7b6f48cb --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentHideShowSupport.java @@ -0,0 +1,114 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.TextView; + +/** + * Demonstration of hiding and showing fragments. + */ +public class FragmentHideShowSupport extends FragmentActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_hide_show_support); + + // The content view embeds two fragments; now retrieve them and attach + // their "hide" button. + FragmentManager fm = getSupportFragmentManager(); + addShowHideListener(R.id.frag1hide, fm.findFragmentById(R.id.fragment1)); + addShowHideListener(R.id.frag2hide, fm.findFragmentById(R.id.fragment2)); + } + + void addShowHideListener(int buttonId, final Fragment fragment) { + final Button button = (Button)findViewById(buttonId); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.setCustomAnimations(android.R.anim.fade_in, + android.R.anim.fade_out); + if (fragment.isHidden()) { + ft.show(fragment); + button.setText("Hide"); + } else { + ft.hide(fragment); + button.setText("Show"); + } + ft.commit(); + } + }); + } + + public static class FirstFragment extends Fragment { + TextView mTextView; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.labeled_text_edit, container, false); + View tv = v.findViewById(R.id.msg); + ((TextView)tv).setText("The fragment saves and restores this text."); + + // Retrieve the text editor, and restore the last saved state if needed. + mTextView = (TextView)v.findViewById(R.id.saved); + if (savedInstanceState != null) { + mTextView.setText(savedInstanceState.getCharSequence("text")); + } + return v; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + // Remember the current text, to restore if we later restart. + outState.putCharSequence("text", mTextView.getText()); + } + } + + public static class SecondFragment extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.labeled_text_edit, container, false); + View tv = v.findViewById(R.id.msg); + ((TextView)tv).setText("The TextView saves and restores this text."); + + // Retrieve the text editor and tell it to save and restore its state. + // Note that you will often set this in the layout XML, but since + // we are sharing our layout with the other fragment we will customize + // it here. + ((TextView)v.findViewById(R.id.saved)).setSaveEnabled(true); + return v; + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentLayoutSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentLayoutSupport.java new file mode 100644 index 000000000..e3355796e --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentLayoutSupport.java @@ -0,0 +1,223 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.Shakespeare; +import com.actionbarsherlock.sample.demos.R; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.app.ListFragment; + +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.ScrollView; +import android.widget.TextView; + +/** + * Demonstration of using fragments to implement different activity layouts. + * This sample provides a different layout (and activity flow) when run in + * landscape. + */ +public class FragmentLayoutSupport extends FragmentActivity { + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.fragment_layout_support); + } + + + /** + * This is a secondary activity, to show what the user has selected + * when the screen is not large enough to show it all in one activity. + */ + + public static class DetailsActivity extends FragmentActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE) { + // If the screen is now in landscape mode, we can show the + // dialog in-line with the list so we don't need this activity. + finish(); + return; + } + + if (savedInstanceState == null) { + // During initial setup, plug in the details fragment. + DetailsFragment details = new DetailsFragment(); + details.setArguments(getIntent().getExtras()); + getSupportFragmentManager().beginTransaction().add( + android.R.id.content, details).commit(); + } + } + } + + + /** + * This is the "top-level" fragment, showing a list of items that the + * user can pick. Upon picking an item, it takes care of displaying the + * data to the user as appropriate based on the currrent UI layout. + */ + + public static class TitlesFragment extends ListFragment { + boolean mDualPane; + int mCurCheckPosition = 0; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Populate list with our static array of titles. + setListAdapter(new ArrayAdapter(getActivity(), + R.layout.simple_list_item_checkable_1, + android.R.id.text1, Shakespeare.TITLES)); + + // Check to see if we have a frame in which to embed the details + // fragment directly in the containing UI. + View detailsFrame = getActivity().findViewById(R.id.details); + mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; + + if (savedInstanceState != null) { + // Restore last state for checked position. + mCurCheckPosition = savedInstanceState.getInt("curChoice", 0); + } + + if (mDualPane) { + // In dual-pane mode, the list view highlights the selected item. + getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); + // Make sure our UI is in the correct state. + showDetails(mCurCheckPosition); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt("curChoice", mCurCheckPosition); + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + showDetails(position); + } + + /** + * Helper function to show the details of a selected item, either by + * displaying a fragment in-place in the current UI, or starting a + * whole new activity in which it is displayed. + */ + void showDetails(int index) { + mCurCheckPosition = index; + + if (mDualPane) { + // We can display everything in-place with fragments, so update + // the list to highlight the selected item and show the data. + getListView().setItemChecked(index, true); + + // Check what fragment is currently shown, replace if needed. + DetailsFragment details = (DetailsFragment) + getFragmentManager().findFragmentById(R.id.details); + if (details == null || details.getShownIndex() != index) { + // Make new fragment to show this selection. + details = DetailsFragment.newInstance(index); + + // Execute a transaction, replacing any existing fragment + // with this one inside the frame. + FragmentTransaction ft = getFragmentManager().beginTransaction(); + ft.replace(R.id.details, details); + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); + ft.commit(); + } + + } else { + // Otherwise we need to launch a new activity to display + // the dialog fragment with selected text. + Intent intent = new Intent(); + intent.setClass(getActivity(), DetailsActivity.class); + intent.putExtra("index", index); + startActivity(intent); + } + } + } + + + /** + * This is the secondary fragment, displaying the details of a particular + * item. + */ + + public static class DetailsFragment extends Fragment { + /** + * Create a new instance of DetailsFragment, initialized to + * show the text at 'index'. + */ + public static DetailsFragment newInstance(int index) { + DetailsFragment f = new DetailsFragment(); + + // Supply index input as an argument. + Bundle args = new Bundle(); + args.putInt("index", index); + f.setArguments(args); + + return f; + } + + public int getShownIndex() { + return getArguments().getInt("index", 0); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + if (container == null) { + // We have different layouts, and in one of them this + // fragment's containing frame doesn't exist. The fragment + // may still be created from its saved state, but there is + // no reason to try to create its view hierarchy because it + // won't be displayed. Note this is not needed -- we could + // just run the code below, where we would create and return + // the view hierarchy; it would just never be used. + return null; + } + + ScrollView scroller = new ScrollView(getActivity()); + TextView text = new TextView(getActivity()); + int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + 4, getActivity().getResources().getDisplayMetrics()); + text.setPadding(padding, padding, padding, padding); + scroller.addView(text); + text.setText(Shakespeare.DIALOGUE[getShownIndex()]); + return scroller; + } + } + +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentListArraySupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentListArraySupport.java new file mode 100644 index 000000000..6ad7367cb --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentListArraySupport.java @@ -0,0 +1,61 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.Shakespeare; + +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.ListFragment; + +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +/** + * Demonstration of using ListFragment to show a list of items + * from a canned array. + */ +public class FragmentListArraySupport extends FragmentActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Create the list fragment and add it as our sole content. + if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) { + ArrayListFragment list = new ArrayListFragment(); + getSupportFragmentManager().beginTransaction().add(android.R.id.content, list).commit(); + } + } + + public static class ArrayListFragment extends ListFragment { + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setListAdapter(new ArrayAdapter(getActivity(), + android.R.layout.simple_list_item_1, Shakespeare.TITLES)); + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + Log.i("FragmentList", "Item clicked: " + id); + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentMenuSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentMenuSupport.java new file mode 100644 index 000000000..9bf2cd514 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentMenuSupport.java @@ -0,0 +1,133 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; + +import android.os.Bundle; +import android.view.MenuInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.CheckBox; + +/** + * Demonstrates how fragments can participate in the options menu. + */ +public class FragmentMenuSupport extends FragmentActivity { + Fragment mFragment1; + Fragment mFragment2; + CheckBox mCheckBox1; + CheckBox mCheckBox2; + + // Update fragment visibility when check boxes are changed. + final OnClickListener mClickListener = new OnClickListener() { + public void onClick(View v) { + updateFragmentVisibility(); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_menu); + + // Make sure the two menu fragments are created. + FragmentManager fm = getSupportFragmentManager(); + FragmentTransaction ft = fm.beginTransaction(); + mFragment1 = fm.findFragmentByTag("f1"); + if (mFragment1 == null) { + mFragment1 = new MenuFragment(); + ft.add(mFragment1, "f1"); + } + mFragment2 = fm.findFragmentByTag("f2"); + if (mFragment2 == null) { + mFragment2 = new Menu2Fragment(); + ft.add(mFragment2, "f2"); + } + ft.commit(); + + // Watch check box clicks. + mCheckBox1 = (CheckBox)findViewById(R.id.menu1); + mCheckBox1.setOnClickListener(mClickListener); + mCheckBox2 = (CheckBox)findViewById(R.id.menu2); + mCheckBox2.setOnClickListener(mClickListener); + + // Make sure fragments start out with correct visibility. + updateFragmentVisibility(); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + // Make sure fragments are updated after check box view state is restored. + updateFragmentVisibility(); + } + + // Update fragment visibility based on current check box state. + void updateFragmentVisibility() { + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + if (mCheckBox1.isChecked()) ft.show(mFragment1); + else ft.hide(mFragment1); + if (mCheckBox2.isChecked()) ft.show(mFragment2); + else ft.hide(mFragment2); + ft.commit(); + } + + /** + * A fragment that displays a menu. This fragment happens to not + * have a UI (it does not implement onCreateView), but it could also + * have one if it wanted. + */ + public static class MenuFragment extends Fragment { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add("Menu 1a").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + menu.add("Menu 1b").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + } + } + + /** + * Second fragment with a menu. + */ + public static class Menu2Fragment extends Fragment { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add("Menu 2").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentPagerMenus.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentPagerMenus.java new file mode 100644 index 000000000..418622d34 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentPagerMenus.java @@ -0,0 +1,96 @@ +package com.actionbarsherlock.sample.demos.app; + +import java.util.Random; +import com.actionbarsherlock.sample.demos.R; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.support.v4.view.ViewPager; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class FragmentPagerMenus extends FragmentActivity { + private static final Random RANDOM = new Random(); + private static final int PAGES = 10; + private static final int MENU_ITEM_RANDOM = 1; + + private ViewPager mPager; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.fragment_pagermenus); + mPager = (ViewPager)findViewById(R.id.pager); + mPager.setAdapter(new TestAdapter(getSupportFragmentManager())); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, MENU_ITEM_RANDOM, 0, "Random"); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == MENU_ITEM_RANDOM) { + mPager.setCurrentItem(RANDOM.nextInt(PAGES)); + return true; + } + return super.onOptionsItemSelected(item); + } + + static final class TestAdapter extends FragmentPagerAdapter { + public TestAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public int getCount() { + return PAGES; + } + + @Override + public Fragment getItem(int position) { + TestFragment f = new TestFragment(); + f.text = String.valueOf(position + 1); + return f; + } + } + + public static class TestFragment extends Fragment { + String text = "???"; + + public TestFragment() { + setRetainInstance(true); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + TextView tv = new TextView(getActivity()); + tv.setGravity(Gravity.CENTER); + tv.setTextSize(50); + tv.setText(text); + return tv; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add(text).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentPagerSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentPagerSupport.java new file mode 100644 index 000000000..b2a88676f --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentPagerSupport.java @@ -0,0 +1,139 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.Cheeses; +import com.actionbarsherlock.sample.demos.R; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.ListFragment; +import android.support.v4.view.ViewPager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ListView; +import android.widget.TextView; + +public class FragmentPagerSupport extends FragmentActivity { + static final int NUM_ITEMS = 10; + + MyAdapter mAdapter; + + ViewPager mPager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_pager); + + mAdapter = new MyAdapter(getSupportFragmentManager()); + + mPager = (ViewPager)findViewById(R.id.pager); + mPager.setAdapter(mAdapter); + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.goto_first); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mPager.setCurrentItem(0); + } + }); + button = (Button)findViewById(R.id.goto_last); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mPager.setCurrentItem(NUM_ITEMS-1); + } + }); + } + + public static class MyAdapter extends FragmentPagerAdapter { + public MyAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public int getCount() { + return NUM_ITEMS; + } + + @Override + public Fragment getItem(int position) { + return ArrayListFragment.newInstance(position); + } + } + + public static class ArrayListFragment extends ListFragment { + int mNum; + + /** + * Create a new instance of CountingFragment, providing "num" + * as an argument. + */ + static ArrayListFragment newInstance(int num) { + ArrayListFragment f = new ArrayListFragment(); + + // Supply num input as an argument. + Bundle args = new Bundle(); + args.putInt("num", num); + f.setArguments(args); + + return f; + } + + /** + * When creating, retrieve this instance's number from its arguments. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mNum = getArguments() != null ? getArguments().getInt("num") : 1; + } + + /** + * The Fragment's UI is just a simple text view showing its + * instance number. + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_pager_list, container, false); + View tv = v.findViewById(R.id.text); + ((TextView)tv).setText("Fragment #" + mNum); + return v; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setListAdapter(new ArrayAdapter(getActivity(), + android.R.layout.simple_list_item_1, Cheeses.sCheeseStrings)); + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + Log.i("FragmentList", "Item clicked: " + id); + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentReceiveResultSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentReceiveResultSupport.java new file mode 100644 index 000000000..bb79ff120 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentReceiveResultSupport.java @@ -0,0 +1,135 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.app.SendResult; +import com.actionbarsherlock.sample.demos.R; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; + +import android.content.Intent; +import android.os.Bundle; +import android.text.Editable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.TextView; + +public class FragmentReceiveResultSupport extends FragmentActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + FrameLayout frame = new FrameLayout(this); + frame.setId(R.id.simple_fragment); + setContentView(frame, lp); + + if (savedInstanceState == null) { + // Do first time initialization -- add fragment. + Fragment newFragment = new ReceiveResultFragment(); + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.add(R.id.simple_fragment, newFragment).commit(); + } + } + + public static class ReceiveResultFragment extends Fragment { + // Definition of the one requestCode we use for receiving resuls. + static final private int GET_CODE = 0; + + private TextView mResults; + + private OnClickListener mGetListener = new OnClickListener() { + public void onClick(View v) { + // Start the activity whose result we want to retrieve. The + // result will come back with request code GET_CODE. + Intent intent = new Intent(getActivity(), SendResult.class); + startActivityForResult(intent, GET_CODE); + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.receive_result, container, false); + + // Retrieve the TextView widget that will display results. + mResults = (TextView)v.findViewById(R.id.results); + + // This allows us to later extend the text buffer. + mResults.setText(mResults.getText(), TextView.BufferType.EDITABLE); + + // Watch for button clicks. + Button getButton = (Button)v.findViewById(R.id.get); + getButton.setOnClickListener(mGetListener); + + return v; + } + + /** + * This method is called when the sending activity has finished, with the + * result it supplied. + */ + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + // You can use the requestCode to select between multiple child + // activities you may have started. Here there is only one thing + // we launch. + if (requestCode == GET_CODE) { + + // We will be adding to our text. + Editable text = (Editable)mResults.getText(); + + // This is a standard resultCode that is sent back if the + // activity doesn't supply an explicit result. It will also + // be returned if the activity failed to launch. + if (resultCode == RESULT_CANCELED) { + text.append("(cancelled)"); + + // Our protocol with the sending activity is that it will send + // text in 'data' as its result. + } else { + text.append("(okay "); + text.append(Integer.toString(resultCode)); + text.append(") "); + if (data != null) { + text.append(data.getAction()); + } + } + + text.append("\n"); + } + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentRetainInstanceSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentRetainInstanceSupport.java new file mode 100644 index 000000000..f10971627 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentRetainInstanceSupport.java @@ -0,0 +1,233 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.ProgressBar; + +/** + * This example shows how you can use a Fragment to easily propagate state + * (such as threads) across activity instances when an activity needs to be + * restarted due to, for example, a configuration change. This is a lot + * easier than using the raw Activity.onRetainNonConfiguratinInstance() API. + */ +public class FragmentRetainInstanceSupport extends FragmentActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // First time init, create the UI. + if (savedInstanceState == null) { + getSupportFragmentManager().beginTransaction().add(android.R.id.content, + new UiFragment()).commit(); + } + } + + /** + * This is a fragment showing UI that will be updated from work done + * in the retained fragment. + */ + public static class UiFragment extends Fragment { + RetainedFragment mWorkFragment; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_retain_instance, container, false); + + // Watch for button clicks. + Button button = (Button)v.findViewById(R.id.restart); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mWorkFragment.restart(); + } + }); + + return v; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + FragmentManager fm = getFragmentManager(); + + // Check to see if we have retained the worker fragment. + mWorkFragment = (RetainedFragment)fm.findFragmentByTag("work"); + + // If not retained (or first time running), we need to create it. + if (mWorkFragment == null) { + mWorkFragment = new RetainedFragment(); + // Tell it who it is working with. + mWorkFragment.setTargetFragment(this, 0); + fm.beginTransaction().add(mWorkFragment, "work").commit(); + } + } + + } + + /** + * This is the Fragment implementation that will be retained across + * activity instances. It represents some ongoing work, here a thread + * we have that sits around incrementing a progress indicator. + */ + public static class RetainedFragment extends Fragment { + ProgressBar mProgressBar; + int mPosition; + boolean mReady = false; + boolean mQuiting = false; + + /** + * This is the thread that will do our work. It sits in a loop running + * the progress up until it has reached the top, then stops and waits. + */ + final Thread mThread = new Thread() { + @Override + public void run() { + // We'll figure the real value out later. + int max = 10000; + + // This thread runs almost forever. + while (true) { + + // Update our shared state with the UI. + synchronized (this) { + // Our thread is stopped if the UI is not ready + // or it has completed its work. + while (!mReady || mPosition >= max) { + if (mQuiting) { + return; + } + try { + wait(); + } catch (InterruptedException e) { + } + } + + // Now update the progress. Note it is important that + // we touch the progress bar with the lock held, so it + // doesn't disappear on us. + mPosition++; + max = mProgressBar.getMax(); + mProgressBar.setProgress(mPosition); + } + + // Normally we would be doing some work, but put a kludge + // here to pretend like we are. + synchronized (this) { + try { + wait(50); + } catch (InterruptedException e) { + } + } + } + } + }; + + /** + * Fragment initialization. We way we want to be retained and + * start our thread. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Tell the framework to try to keep this fragment around + // during a configuration change. + setRetainInstance(true); + + // Start up the worker thread. + mThread.start(); + } + + /** + * This is called when the Fragment's Activity is ready to go, after + * its content view has been installed; it is called both after + * the initial fragment creation and after the fragment is re-attached + * to a new activity. + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Retrieve the progress bar from the target's view hierarchy. + mProgressBar = (ProgressBar)getTargetFragment().getView().findViewById( + R.id.progress_horizontal); + + // We are ready for our thread to go. + synchronized (mThread) { + mReady = true; + mThread.notify(); + } + } + + /** + * This is called when the fragment is going away. It is NOT called + * when the fragment is being propagated between activity instances. + */ + @Override + public void onDestroy() { + // Make the thread go away. + synchronized (mThread) { + mReady = false; + mQuiting = true; + mThread.notify(); + } + + super.onDestroy(); + } + + /** + * This is called right before the fragment is detached from its + * current activity instance. + */ + @Override + public void onDetach() { + // This fragment is being detached from its activity. We need + // to make sure its thread is not going to touch any activity + // state after returning from this function. + synchronized (mThread) { + mProgressBar = null; + mReady = false; + mThread.notify(); + } + + super.onDetach(); + } + + /** + * API for our UI to restart the progress thread. + */ + public void restart() { + synchronized (mThread) { + mPosition = 0; + mThread.notify(); + } + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentStackSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentStackSupport.java new file mode 100644 index 000000000..f8648a3c1 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentStackSupport.java @@ -0,0 +1,125 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.TextView; + +public class FragmentStackSupport extends FragmentActivity { + int mStackLevel = 1; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_stack); + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.new_fragment); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + addFragmentToStack(); + } + }); + + if (savedInstanceState == null) { + // Do first time initialization -- add initial fragment. + Fragment newFragment = CountingFragment.newInstance(mStackLevel); + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.add(R.id.simple_fragment, newFragment).commit(); + } else { + mStackLevel = savedInstanceState.getInt("level"); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt("level", mStackLevel); + } + + + void addFragmentToStack() { + mStackLevel++; + + // Instantiate a new fragment. + Fragment newFragment = CountingFragment.newInstance(mStackLevel); + + // Add the fragment to the activity, pushing this transaction + // on to the back stack. + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.replace(R.id.simple_fragment, newFragment); + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + ft.addToBackStack(null); + ft.commit(); + } + + + + public static class CountingFragment extends Fragment { + int mNum; + + /** + * Create a new instance of CountingFragment, providing "num" + * as an argument. + */ + static CountingFragment newInstance(int num) { + CountingFragment f = new CountingFragment(); + + // Supply num input as an argument. + Bundle args = new Bundle(); + args.putInt("num", num); + f.setArguments(args); + + return f; + } + + /** + * When creating, retrieve this instance's number from its arguments. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mNum = getArguments() != null ? getArguments().getInt("num") : 1; + } + + /** + * The Fragment's UI is just a simple text view showing its + * instance number. + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.hello_world, container, false); + View tv = v.findViewById(R.id.text); + ((TextView)tv).setText("Fragment #" + mNum); + tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb)); + return v; + } + } + +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentStatePagerSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentStatePagerSupport.java new file mode 100644 index 000000000..f4ebf78b9 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentStatePagerSupport.java @@ -0,0 +1,140 @@ +/* + * 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.sample.demos.app; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; +import android.support.v4.app.ListFragment; +import android.support.v4.view.ViewPager; + +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ListView; +import android.widget.TextView; + +import com.actionbarsherlock.sample.demos.Cheeses; +import com.actionbarsherlock.sample.demos.R; + +public class FragmentStatePagerSupport extends FragmentActivity { + static final int NUM_ITEMS = 10; + + MyAdapter mAdapter; + + ViewPager mPager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_pager); + + mAdapter = new MyAdapter(getSupportFragmentManager()); + + mPager = (ViewPager)findViewById(R.id.pager); + mPager.setAdapter(mAdapter); + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.goto_first); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mPager.setCurrentItem(0); + } + }); + button = (Button)findViewById(R.id.goto_last); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mPager.setCurrentItem(NUM_ITEMS-1); + } + }); + } + + public static class MyAdapter extends FragmentStatePagerAdapter { + public MyAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public int getCount() { + return NUM_ITEMS; + } + + @Override + public Fragment getItem(int position) { + return ArrayListFragment.newInstance(position); + } + } + + public static class ArrayListFragment extends ListFragment { + int mNum; + + /** + * Create a new instance of CountingFragment, providing "num" + * as an argument. + */ + static ArrayListFragment newInstance(int num) { + ArrayListFragment f = new ArrayListFragment(); + + // Supply num input as an argument. + Bundle args = new Bundle(); + args.putInt("num", num); + f.setArguments(args); + + return f; + } + + /** + * When creating, retrieve this instance's number from its arguments. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mNum = getArguments() != null ? getArguments().getInt("num") : 1; + } + + /** + * The Fragment's UI is just a simple text view showing its + * instance number. + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_pager_list, container, false); + View tv = v.findViewById(R.id.text); + ((TextView)tv).setText("Fragment #" + mNum); + return v; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setListAdapter(new ArrayAdapter(getActivity(), + android.R.layout.simple_list_item_1, Cheeses.sCheeseStrings)); + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + Log.i("FragmentList", "Item clicked: " + id); + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentTabs.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentTabs.java new file mode 100644 index 000000000..aec827e70 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentTabs.java @@ -0,0 +1,171 @@ +/* + * 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.sample.demos.app; + + +import java.util.HashMap; + +import com.actionbarsherlock.sample.demos.R; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.view.View; +import android.widget.TabHost; + +/** + * This demonstrates how you can implement switching between the tabs of a + * TabHost through fragments. It uses a trick (see the code below) to allow + * the tabs to switch between fragments instead of simple views. + */ +public class FragmentTabs extends FragmentActivity { + TabHost mTabHost; + TabManager mTabManager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.fragment_tabs); + mTabHost = (TabHost)findViewById(android.R.id.tabhost); + mTabHost.setup(); + + mTabManager = new TabManager(this, mTabHost, R.id.realtabcontent); + + mTabManager.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"), + FragmentStackSupport.CountingFragment.class, null); + mTabManager.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"), + LoaderCursorSupport.CursorLoaderListFragment.class, null); + mTabManager.addTab(mTabHost.newTabSpec("custom").setIndicator("Custom"), + LoaderCustomSupport.AppListFragment.class, null); + mTabManager.addTab(mTabHost.newTabSpec("throttle").setIndicator("Throttle"), + LoaderThrottleSupport.ThrottledLoaderListFragment.class, null); + + if (savedInstanceState != null) { + mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab")); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString("tab", mTabHost.getCurrentTabTag()); + } + + /** + * This is a helper class that implements a generic mechanism for + * associating fragments with the tabs in a tab host. It relies on a + * trick. Normally a tab host has a simple API for supplying a View or + * Intent that each tab will show. This is not sufficient for switching + * between fragments. So instead we make the content part of the tab host + * 0dp high (it is not shown) and the TabManager supplies its own dummy + * view to show as the tab content. It listens to changes in tabs, and takes + * care of switch to the correct fragment shown in a separate content area + * whenever the selected tab changes. + */ + public static class TabManager implements TabHost.OnTabChangeListener { + private final FragmentActivity mActivity; + private final TabHost mTabHost; + private final int mContainerId; + private final HashMap mTabs = new HashMap(); + TabInfo mLastTab; + + static final class TabInfo { + private final String tag; + private final Class clss; + private final Bundle args; + private Fragment fragment; + + TabInfo(String _tag, Class _class, Bundle _args) { + tag = _tag; + clss = _class; + args = _args; + } + } + + static class DummyTabFactory implements TabHost.TabContentFactory { + private final Context mContext; + + public DummyTabFactory(Context context) { + mContext = context; + } + + @Override + public View createTabContent(String tag) { + View v = new View(mContext); + v.setMinimumWidth(0); + v.setMinimumHeight(0); + return v; + } + } + + public TabManager(FragmentActivity activity, TabHost tabHost, int containerId) { + mActivity = activity; + mTabHost = tabHost; + mContainerId = containerId; + mTabHost.setOnTabChangedListener(this); + } + + public void addTab(TabHost.TabSpec tabSpec, Class clss, Bundle args) { + tabSpec.setContent(new DummyTabFactory(mActivity)); + String tag = tabSpec.getTag(); + + TabInfo info = new TabInfo(tag, clss, args); + + // Check to see if we already have a fragment for this tab, probably + // from a previously saved state. If so, deactivate it, because our + // initial state is that a tab isn't shown. + info.fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag); + if (info.fragment != null && !info.fragment.isDetached()) { + FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction(); + ft.detach(info.fragment); + ft.commit(); + } + + mTabs.put(tag, info); + mTabHost.addTab(tabSpec); + } + + @Override + public void onTabChanged(String tabId) { + TabInfo newTab = mTabs.get(tabId); + if (mLastTab != newTab) { + FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction(); + if (mLastTab != null) { + if (mLastTab.fragment != null) { + ft.detach(mLastTab.fragment); + } + } + if (newTab != null) { + if (newTab.fragment == null) { + newTab.fragment = Fragment.instantiate(mActivity, + newTab.clss.getName(), newTab.args); + ft.add(mContainerId, newTab.fragment, newTab.tag); + } else { + ft.attach(newTab.fragment); + } + } + + mLastTab = newTab; + ft.commit(); + mActivity.getSupportFragmentManager().executePendingTransactions(); + } + } + } +} + diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentTabsPager.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentTabsPager.java new file mode 100644 index 000000000..3bcfaf05c --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/FragmentTabsPager.java @@ -0,0 +1,178 @@ +/* + * 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.sample.demos.app; + +import com.actionbarsherlock.sample.demos.R; + +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; +import android.view.View; +import android.widget.TabHost; + +import java.util.ArrayList; + +/** + * Demonstrates combining a TabHost with a ViewPager to implement a tab UI + * that switches between tabs and also allows the user to perform horizontal + * flicks to move between the tabs. + */ +public class FragmentTabsPager extends FragmentActivity { + TabHost mTabHost; + ViewPager mViewPager; + TabsAdapter mTabsAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.fragment_tabs_pager); + mTabHost = (TabHost)findViewById(android.R.id.tabhost); + mTabHost.setup(); + + mViewPager = (ViewPager)findViewById(R.id.pager); + + mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager); + mTabsAdapter.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"), + FragmentStackSupport.CountingFragment.class, null); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR) { + mTabsAdapter.addTab(mTabHost.newTabSpec("simple2").setIndicator("Simple 2"), + FragmentStackSupport.CountingFragment.class, null); + mTabsAdapter.addTab(mTabHost.newTabSpec("simple3").setIndicator("Simple 3"), + FragmentStackSupport.CountingFragment.class, null); + mTabsAdapter.addTab(mTabHost.newTabSpec("simple4").setIndicator("Simple 4"), + FragmentStackSupport.CountingFragment.class, null); + } else { + mTabsAdapter.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"), + LoaderCursorSupport.CursorLoaderListFragment.class, null); + mTabsAdapter.addTab(mTabHost.newTabSpec("custom").setIndicator("Custom"), + LoaderCustomSupport.AppListFragment.class, null); + mTabsAdapter.addTab(mTabHost.newTabSpec("throttle").setIndicator("Throttle"), + LoaderThrottleSupport.ThrottledLoaderListFragment.class, null); + } + + if (savedInstanceState != null) { + mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab")); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString("tab", mTabHost.getCurrentTabTag()); + } + + /** + * This is a helper class that implements the management of tabs and all + * details of connecting a ViewPager with associated TabHost. It relies on a + * trick. Normally a tab host has a simple API for supplying a View or + * Intent that each tab will show. This is not sufficient for switching + * between pages. So instead we make the content part of the tab host + * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy + * view to show as the tab content. It listens to changes in tabs, and takes + * care of switch to the correct paged in the ViewPager whenever the selected + * tab changes. + */ + public static class TabsAdapter extends FragmentPagerAdapter + implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener { + private final Context mContext; + private final TabHost mTabHost; + private final ViewPager mViewPager; + private final ArrayList mTabs = new ArrayList(); + + static final class TabInfo { + private final String tag; + private final Class clss; + private final Bundle args; + + TabInfo(String _tag, Class _class, Bundle _args) { + tag = _tag; + clss = _class; + args = _args; + } + } + + static class DummyTabFactory implements TabHost.TabContentFactory { + private final Context mContext; + + public DummyTabFactory(Context context) { + mContext = context; + } + + @Override + public View createTabContent(String tag) { + View v = new View(mContext); + v.setMinimumWidth(0); + v.setMinimumHeight(0); + return v; + } + } + + public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) { + super(activity.getSupportFragmentManager()); + mContext = activity; + mTabHost = tabHost; + mViewPager = pager; + mTabHost.setOnTabChangedListener(this); + mViewPager.setAdapter(this); + mViewPager.setOnPageChangeListener(this); + } + + public void addTab(TabHost.TabSpec tabSpec, Class clss, Bundle args) { + tabSpec.setContent(new DummyTabFactory(mContext)); + String tag = tabSpec.getTag(); + + TabInfo info = new TabInfo(tag, clss, args); + mTabs.add(info); + mTabHost.addTab(tabSpec); + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mTabs.size(); + } + + @Override + public Fragment getItem(int position) { + TabInfo info = mTabs.get(position); + return Fragment.instantiate(mContext, info.clss.getName(), info.args); + } + + @Override + public void onTabChanged(String tabId) { + int position = mTabHost.getCurrentTab(); + mViewPager.setCurrentItem(position); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + mTabHost.setCurrentTab(position); + } + + @Override + public void onPageScrollStateChanged(int state) { + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/LoaderCursorSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/LoaderCursorSupport.java new file mode 100644 index 000000000..e477afc03 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/LoaderCursorSupport.java @@ -0,0 +1,170 @@ +/* + * 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.sample.demos.app; + +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.ListFragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.view.Menu; +import android.support.v4.widget.SimpleCursorAdapter; + +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.ContactsContract.Contacts; +import android.text.TextUtils; +import android.util.Log; +import android.view.MenuInflater; +import android.view.View; +import android.widget.ListView; + +/** + * Demonstration of the use of a CursorLoader to load and display contacts + * data in a fragment. + */ +public class LoaderCursorSupport extends FragmentActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FragmentManager fm = getSupportFragmentManager(); + + // Create the list fragment and add it as our sole content. + if (fm.findFragmentById(android.R.id.content) == null) { + CursorLoaderListFragment list = new CursorLoaderListFragment(); + fm.beginTransaction().add(android.R.id.content, list).commit(); + } + } + + + public static class CursorLoaderListFragment extends ListFragment + implements LoaderManager.LoaderCallbacks { + + // This is the Adapter being used to display the list's data. + SimpleCursorAdapter mAdapter; + + // If non-null, this is the current filter the user has provided. + String mCurFilter; + + @Override public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText("No phone numbers"); + + // We have a menu item to show in action bar. + setHasOptionsMenu(true); + + // Create an empty adapter we will use to display the loaded data. + mAdapter = new SimpleCursorAdapter(getActivity(), + android.R.layout.simple_list_item_2, null, + new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, + new int[] { android.R.id.text1, android.R.id.text2 }, 0); + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + // Place an action bar item for searching. + //MenuItem item = menu.add("Search"); + //item.setIcon(android.R.drawable.ic_menu_search); + //item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + //SearchView sv = new SearchView(getActivity()); + //sv.setOnQueryTextListener(this); + //item.setActionView(sv); + } + + public boolean onQueryTextChange(String newText) { + // Called when the action bar search text has changed. Update + // the search filter, and restart the loader to do a new query + // with this filter. + mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; + getLoaderManager().restartLoader(0, null, this); + return true; + } + + @Override public void onListItemClick(ListView l, View v, int position, long id) { + // Insert desired behavior here. + Log.i("FragmentComplexList", "Item clicked: " + id); + } + + // These are the Contacts rows that we will retrieve. + static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { + Contacts._ID, + Contacts.DISPLAY_NAME, + Contacts.CONTACT_STATUS, + Contacts.CONTACT_PRESENCE, + Contacts.PHOTO_ID, + Contacts.LOOKUP_KEY, + }; + + public Loader onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + // First, pick the base URI to use depending on whether we are + // currently filtering. + Uri baseUri; + if (mCurFilter != null) { + baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, + Uri.encode(mCurFilter)); + } else { + baseUri = Contacts.CONTENT_URI; + } + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + + Contacts.DISPLAY_NAME + " != '' ))"; + return new CursorLoader(getActivity(), baseUri, + CONTACTS_SUMMARY_PROJECTION, select, null, + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); + } + + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + public void onLoaderReset(Loader loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } + } + +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/LoaderCustomSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/LoaderCustomSupport.java new file mode 100644 index 000000000..9652edf11 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/LoaderCustomSupport.java @@ -0,0 +1,453 @@ +/* + * 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.sample.demos.app; + +import java.io.File; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.ListFragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.AsyncTaskLoader; +import android.support.v4.content.Loader; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; +import com.actionbarsherlock.sample.demos.R; + +/** + * Demonstration of the implementation of a custom Loader. + */ +public class LoaderCustomSupport extends FragmentActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FragmentManager fm = getSupportFragmentManager(); + + // Create the list fragment and add it as our sole content. + if (fm.findFragmentById(android.R.id.content) == null) { + AppListFragment list = new AppListFragment(); + fm.beginTransaction().add(android.R.id.content, list).commit(); + } + } + + + /** + * This class holds the per-item data in our Loader. + */ + public static class AppEntry { + public AppEntry(AppListLoader loader, ApplicationInfo info) { + mLoader = loader; + mInfo = info; + mApkFile = new File(info.sourceDir); + } + + public ApplicationInfo getApplicationInfo() { + return mInfo; + } + + public String getLabel() { + return mLabel; + } + + public Drawable getIcon() { + if (mIcon == null) { + if (mApkFile.exists()) { + mIcon = mInfo.loadIcon(mLoader.mPm); + return mIcon; + } else { + mMounted = false; + } + } else if (!mMounted) { + // If the app wasn't mounted but is now mounted, reload + // its icon. + if (mApkFile.exists()) { + mMounted = true; + mIcon = mInfo.loadIcon(mLoader.mPm); + return mIcon; + } + } else { + return mIcon; + } + + return mLoader.getContext().getResources().getDrawable( + android.R.drawable.sym_def_app_icon); + } + + @Override public String toString() { + return mLabel; + } + + void loadLabel(Context context) { + if (mLabel == null || !mMounted) { + if (!mApkFile.exists()) { + mMounted = false; + mLabel = mInfo.packageName; + } else { + mMounted = true; + CharSequence label = mInfo.loadLabel(context.getPackageManager()); + mLabel = label != null ? label.toString() : mInfo.packageName; + } + } + } + + private final AppListLoader mLoader; + private final ApplicationInfo mInfo; + private final File mApkFile; + private String mLabel; + private Drawable mIcon; + private boolean mMounted; + } + + /** + * Perform alphabetical comparison of application entry objects. + */ + public static final Comparator ALPHA_COMPARATOR = new Comparator() { + private final Collator sCollator = Collator.getInstance(); + @Override + public int compare(AppEntry object1, AppEntry object2) { + return sCollator.compare(object1.getLabel(), object2.getLabel()); + } + }; + + /** + * Helper for determining if the configuration has changed in an interesting + * way so we need to rebuild the app list. + */ + public static class InterestingConfigChanges { + final Configuration mLastConfiguration = new Configuration(); + int mLastDensity; + + boolean applyNewConfig(Resources res) { + int configChanges = mLastConfiguration.updateFrom(res.getConfiguration()); + boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi; + if (densityChanged || (configChanges&(ActivityInfo.CONFIG_LOCALE + |ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) { + mLastDensity = res.getDisplayMetrics().densityDpi; + return true; + } + return false; + } + } + + /** + * Helper class to look for interesting changes to the installed apps + * so that the loader can be updated. + */ + public static class PackageIntentReceiver extends BroadcastReceiver { + final AppListLoader mLoader; + + public PackageIntentReceiver(AppListLoader loader) { + mLoader = loader; + IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addDataScheme("package"); + mLoader.getContext().registerReceiver(this, filter); + // Register for events related to sdcard installation. + IntentFilter sdFilter = new IntentFilter(); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + mLoader.getContext().registerReceiver(this, sdFilter); + } + + @Override public void onReceive(Context context, Intent intent) { + // Tell the loader about the change. + mLoader.onContentChanged(); + } + } + + /** + * A custom Loader that loads all of the installed applications. + */ + public static class AppListLoader extends AsyncTaskLoader> { + final InterestingConfigChanges mLastConfig = new InterestingConfigChanges(); + final PackageManager mPm; + + List mApps; + PackageIntentReceiver mPackageObserver; + + public AppListLoader(Context context) { + super(context); + + // Retrieve the package manager for later use; note we don't + // use 'context' directly but instead the save global application + // context returned by getContext(). + mPm = getContext().getPackageManager(); + } + + /** + * This is where the bulk of our work is done. This function is + * called in a background thread and should generate a new set of + * data to be published by the loader. + */ + @Override public List loadInBackground() { + // Retrieve all known applications. + List apps = mPm.getInstalledApplications( + PackageManager.GET_UNINSTALLED_PACKAGES | + PackageManager.GET_DISABLED_COMPONENTS); + if (apps == null) { + apps = new ArrayList(); + } + + final Context context = getContext(); + + // Create corresponding array of entries and load their labels. + List entries = new ArrayList(apps.size()); + for (int i=0; i apps) { + if (isReset()) { + // An async query came in while the loader is stopped. We + // don't need the result. + if (apps != null) { + onReleaseResources(apps); + } + } + List oldApps = apps; + mApps = apps; + + if (isStarted()) { + // If the Loader is currently started, we can immediately + // deliver its results. + super.deliverResult(apps); + } + + // At this point we can release the resources associated with + // 'oldApps' if needed; now that the new result is delivered we + // know that it is no longer in use. + if (oldApps != null) { + onReleaseResources(oldApps); + } + } + + /** + * Handles a request to start the Loader. + */ + @Override protected void onStartLoading() { + if (mApps != null) { + // If we currently have a result available, deliver it + // immediately. + deliverResult(mApps); + } + + // Start watching for changes in the app data. + if (mPackageObserver == null) { + mPackageObserver = new PackageIntentReceiver(this); + } + + // Has something interesting in the configuration changed since we + // last built the app list? + boolean configChange = mLastConfig.applyNewConfig(getContext().getResources()); + + if (takeContentChanged() || mApps == null || configChange) { + // If the data has changed since the last time it was loaded + // or is not currently available, start a load. + forceLoad(); + } + } + + /** + * Handles a request to stop the Loader. + */ + @Override protected void onStopLoading() { + // Attempt to cancel the current load task if possible. + cancelLoad(); + } + + /** + * Handles a request to cancel a load. + */ + @Override public void onCanceled(List apps) { + super.onCanceled(apps); + + // At this point we can release the resources associated with 'apps' + // if needed. + onReleaseResources(apps); + } + + /** + * Handles a request to completely reset the Loader. + */ + @Override protected void onReset() { + super.onReset(); + + // Ensure the loader is stopped + onStopLoading(); + + // At this point we can release the resources associated with 'apps' + // if needed. + if (mApps != null) { + onReleaseResources(mApps); + mApps = null; + } + + // Stop monitoring for changes. + if (mPackageObserver != null) { + getContext().unregisterReceiver(mPackageObserver); + mPackageObserver = null; + } + } + + /** + * Helper function to take care of releasing resources associated + * with an actively loaded data set. + */ + protected void onReleaseResources(List apps) { + // For a simple List<> there is nothing to do. For something + // like a Cursor, we would close it here. + } + } + + + + public static class AppListAdapter extends ArrayAdapter { + private final LayoutInflater mInflater; + + public AppListAdapter(Context context) { + super(context, android.R.layout.simple_list_item_2); + mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + public void setData(List data) { + clear(); + if (data != null) { + for (AppEntry item : data) { + add(item); + } + } + } + + /** + * Populate new items in the list. + */ + @Override public View getView(int position, View convertView, ViewGroup parent) { + View view; + + if (convertView == null) { + view = mInflater.inflate(R.layout.list_item_icon_text, parent, false); + } else { + view = convertView; + } + + AppEntry item = getItem(position); + ((ImageView)view.findViewById(R.id.icon)).setImageDrawable(item.getIcon()); + ((TextView)view.findViewById(R.id.text)).setText(item.getLabel()); + + return view; + } + } + + public static class AppListFragment extends ListFragment + implements LoaderManager.LoaderCallbacks> { + + // This is the Adapter being used to display the list's data. + AppListAdapter mAdapter; + + // If non-null, this is the current filter the user has provided. + String mCurFilter; + + @Override public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText("No applications"); + + // We have a menu item to show in action bar. + setHasOptionsMenu(true); + + // Create an empty adapter we will use to display the loaded data. + mAdapter = new AppListAdapter(getActivity()); + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + @Override public void onListItemClick(ListView l, View v, int position, long id) { + // Insert desired behavior here. + Log.i("LoaderCustom", "Item clicked: " + id); + } + + @Override public Loader> onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader with no arguments, so it is simple. + return new AppListLoader(getActivity()); + } + + @Override public void onLoadFinished(Loader> loader, List data) { + // Set the new data in the adapter. + mAdapter.setData(data); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + @Override public void onLoaderReset(Loader> loader) { + // Clear the data in the adapter. + mAdapter.setData(null); + } + } + +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/LoaderThrottleSupport.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/LoaderThrottleSupport.java new file mode 100644 index 000000000..a15962259 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/LoaderThrottleSupport.java @@ -0,0 +1,525 @@ +/* + * 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.sample.demos.app; + + +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.ListFragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.support.v4.widget.SimpleCursorAdapter; + +import android.content.ContentProvider; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.provider.BaseColumns; +import android.text.TextUtils; +import android.util.Log; +import android.view.MenuInflater; +import android.view.View; +import android.widget.ListView; + +import java.util.HashMap; + +/** + * Demonstration of bottom to top implementation of a content provider holding + * structured data through displaying it in the UI, using throttling to reduce + * the number of queries done when its data changes. + */ +public class LoaderThrottleSupport extends FragmentActivity { + // Debugging. + static final String TAG = "LoaderThrottle"; + + /** + * The authority we use to get to our sample provider. + */ + public static final String AUTHORITY = "com.actionbarsherlock.sample.demos.apis.LoaderThrottle"; + + /** + * Definition of the contract for the main table of our provider. + */ + public static final class MainTable implements BaseColumns { + + // This class cannot be instantiated + private MainTable() {} + + /** + * The table name offered by this provider + */ + public static final String TABLE_NAME = "main"; + + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/main"); + + /** + * The content URI base for a single row of data. Callers must + * append a numeric row id to this Uri to retrieve a row + */ + public static final Uri CONTENT_ID_URI_BASE + = Uri.parse("content://" + AUTHORITY + "/main/"); + + /** + * The MIME type of {@link #CONTENT_URI}. + */ + public static final String CONTENT_TYPE + = "vnd.android.cursor.dir/vnd.example.api-demos-throttle"; + + /** + * The MIME type of a {@link #CONTENT_URI} sub-directory of a single row. + */ + public static final String CONTENT_ITEM_TYPE + = "vnd.android.cursor.item/vnd.example.api-demos-throttle"; + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "data COLLATE LOCALIZED ASC"; + + /** + * Column name for the single column holding our data. + *

Type: TEXT

+ */ + public static final String COLUMN_NAME_DATA = "data"; + } + + /** + * This class helps open, create, and upgrade the database file. + */ + static class DatabaseHelper extends SQLiteOpenHelper { + + private static final String DATABASE_NAME = "loader_throttle.db"; + private static final int DATABASE_VERSION = 2; + + DatabaseHelper(Context context) { + + // calls the super constructor, requesting the default cursor factory. + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + /** + * + * Creates the underlying database with table name and column names taken from the + * NotePad class. + */ + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE " + MainTable.TABLE_NAME + " (" + + MainTable._ID + " INTEGER PRIMARY KEY," + + MainTable.COLUMN_NAME_DATA + " TEXT" + + ");"); + } + + /** + * + * Demonstrates that the provider must consider what happens when the + * underlying datastore is changed. In this sample, the database is upgraded the database + * by destroying the existing data. + * A real application should upgrade the database in place. + */ + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + + // Logs that the database is being upgraded + Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + + newVersion + ", which will destroy all old data"); + + // Kills the table and existing data + db.execSQL("DROP TABLE IF EXISTS notes"); + + // Recreates the database with a new version + onCreate(db); + } + } + + /** + * A very simple implementation of a content provider. + */ + public static class SimpleProvider extends ContentProvider { + // A projection map used to select columns from the database + private final HashMap mNotesProjectionMap; + // Uri matcher to decode incoming URIs. + private final UriMatcher mUriMatcher; + + // The incoming URI matches the main table URI pattern + private static final int MAIN = 1; + // The incoming URI matches the main table row ID URI pattern + private static final int MAIN_ID = 2; + + // Handle to a new DatabaseHelper. + private DatabaseHelper mOpenHelper; + + /** + * Global provider initialization. + */ + public SimpleProvider() { + // Create and initialize URI matcher. + mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + mUriMatcher.addURI(AUTHORITY, MainTable.TABLE_NAME, MAIN); + mUriMatcher.addURI(AUTHORITY, MainTable.TABLE_NAME + "/#", MAIN_ID); + + // Create and initialize projection map for all columns. This is + // simply an identity mapping. + mNotesProjectionMap = new HashMap(); + mNotesProjectionMap.put(MainTable._ID, MainTable._ID); + mNotesProjectionMap.put(MainTable.COLUMN_NAME_DATA, MainTable.COLUMN_NAME_DATA); + } + + /** + * Perform provider creation. + */ + @Override + public boolean onCreate() { + mOpenHelper = new DatabaseHelper(getContext()); + // Assumes that any failures will be reported by a thrown exception. + return true; + } + + /** + * Handle incoming queries. + */ + @Override + public Cursor query(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + + // Constructs a new query builder and sets its table name + SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); + qb.setTables(MainTable.TABLE_NAME); + + switch (mUriMatcher.match(uri)) { + case MAIN: + // If the incoming URI is for main table. + qb.setProjectionMap(mNotesProjectionMap); + break; + + case MAIN_ID: + // The incoming URI is for a single row. + qb.setProjectionMap(mNotesProjectionMap); + qb.appendWhere(MainTable._ID + "=?"); + String[] newSelectionArgs = new String[selectionArgs.length + 1]; + System.arraycopy(selectionArgs, 0, newSelectionArgs, 0, selectionArgs.length); + newSelectionArgs[selectionArgs.length] = uri.getLastPathSegment(); + selectionArgs = newSelectionArgs; + break; + + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + + + if (TextUtils.isEmpty(sortOrder)) { + sortOrder = MainTable.DEFAULT_SORT_ORDER; + } + + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + + Cursor c = qb.query(db, projection, selection, selectionArgs, + null /* no group */, null /* no filter */, sortOrder); + + c.setNotificationUri(getContext().getContentResolver(), uri); + return c; + } + + /** + * Return the MIME type for an known URI in the provider. + */ + @Override + public String getType(Uri uri) { + switch (mUriMatcher.match(uri)) { + case MAIN: + return MainTable.CONTENT_TYPE; + case MAIN_ID: + return MainTable.CONTENT_ITEM_TYPE; + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + } + + /** + * Handler inserting new data. + */ + @Override + public Uri insert(Uri uri, ContentValues initialValues) { + if (mUriMatcher.match(uri) != MAIN) { + // Can only insert into to main URI. + throw new IllegalArgumentException("Unknown URI " + uri); + } + + ContentValues values; + + if (initialValues != null) { + values = new ContentValues(initialValues); + } else { + values = new ContentValues(); + } + + if (values.containsKey(MainTable.COLUMN_NAME_DATA) == false) { + values.put(MainTable.COLUMN_NAME_DATA, ""); + } + + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + + long rowId = db.insert(MainTable.TABLE_NAME, null, values); + + // If the insert succeeded, the row ID exists. + if (rowId > 0) { + Uri noteUri = ContentUris.withAppendedId(MainTable.CONTENT_ID_URI_BASE, rowId); + getContext().getContentResolver().notifyChange(noteUri, null); + return noteUri; + } + + throw new SQLException("Failed to insert row into " + uri); + } + + /** + * Handle deleting data. + */ + @Override + public int delete(Uri uri, String where, String[] whereArgs) { + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + String finalWhere; + + int count; + + switch (mUriMatcher.match(uri)) { + case MAIN: + // If URI is main table, delete uses incoming where clause and args. + count = db.delete(MainTable.TABLE_NAME, where, whereArgs); + break; + + // If the incoming URI matches a single note ID, does the delete based on the + // incoming data, but modifies the where clause to restrict it to the + // particular note ID. + case MAIN_ID: + // If URI is for a particular row ID, delete is based on incoming + // data but modified to restrict to the given ID. + finalWhere = DatabaseUtils.concatenateWhere( + MainTable._ID + " = " + ContentUris.parseId(uri), where); + count = db.delete(MainTable.TABLE_NAME, finalWhere, whereArgs); + break; + + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + + getContext().getContentResolver().notifyChange(uri, null); + + return count; + } + + /** + * Handle updating data. + */ + @Override + public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + int count; + String finalWhere; + + switch (mUriMatcher.match(uri)) { + case MAIN: + // If URI is main table, update uses incoming where clause and args. + count = db.update(MainTable.TABLE_NAME, values, where, whereArgs); + break; + + case MAIN_ID: + // If URI is for a particular row ID, update is based on incoming + // data but modified to restrict to the given ID. + finalWhere = DatabaseUtils.concatenateWhere( + MainTable._ID + " = " + ContentUris.parseId(uri), where); + count = db.update(MainTable.TABLE_NAME, values, finalWhere, whereArgs); + break; + + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + + getContext().getContentResolver().notifyChange(uri, null); + + return count; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FragmentManager fm = getSupportFragmentManager(); + + // Create the list fragment and add it as our sole content. + if (fm.findFragmentById(android.R.id.content) == null) { + ThrottledLoaderListFragment list = new ThrottledLoaderListFragment(); + fm.beginTransaction().add(android.R.id.content, list).commit(); + } + } + + public static class ThrottledLoaderListFragment extends ListFragment + implements LoaderManager.LoaderCallbacks { + + // Menu identifiers + static final int POPULATE_ID = Menu.FIRST; + static final int CLEAR_ID = Menu.FIRST+1; + + // This is the Adapter being used to display the list's data. + SimpleCursorAdapter mAdapter; + + // If non-null, this is the current filter the user has provided. + String mCurFilter; + + // Task we have running to populate the database. + AsyncTask mPopulatingTask; + + @Override public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + setEmptyText("No data. Select 'Populate' to fill with data from Z to A at a rate of 4 per second."); + setHasOptionsMenu(true); + + // Create an empty adapter we will use to display the loaded data. + mAdapter = new SimpleCursorAdapter(getActivity(), + android.R.layout.simple_list_item_1, null, + new String[] { MainTable.COLUMN_NAME_DATA }, + new int[] { android.R.id.text1 }, 0); + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add(Menu.NONE, POPULATE_ID, 0, "Populate") + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + menu.add(Menu.NONE, CLEAR_ID, 0, "Clear") + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + } + + @Override public boolean onOptionsItemSelected(MenuItem item) { + final ContentResolver cr = getActivity().getContentResolver(); + + switch (item.getItemId()) { + case POPULATE_ID: + if (mPopulatingTask != null) { + mPopulatingTask.cancel(false); + } + mPopulatingTask = new AsyncTask() { + @Override protected Void doInBackground(Void... params) { + for (char c='Z'; c>='A'; c--) { + if (isCancelled()) { + break; + } + StringBuilder builder = new StringBuilder("Data "); + builder.append(c); + ContentValues values = new ContentValues(); + values.put(MainTable.COLUMN_NAME_DATA, builder.toString()); + cr.insert(MainTable.CONTENT_URI, values); + // Wait a bit between each insert. + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + } + return null; + } + }; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + mPopulatingTask.execute((Void[])null); + } else { + AsyncTaskCompatHoneycomb.executeOnExecutor(mPopulatingTask); + } + return true; + + case CLEAR_ID: + if (mPopulatingTask != null) { + mPopulatingTask.cancel(false); + mPopulatingTask = null; + } + AsyncTask task = new AsyncTask() { + @Override protected Void doInBackground(Void... params) { + cr.delete(MainTable.CONTENT_URI, null, null); + return null; + } + }; + task.execute((Void[])null); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + + static class AsyncTaskCompatHoneycomb { + static void executeOnExecutor(AsyncTask task) { + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null); + } + } + + @Override public void onListItemClick(ListView l, View v, int position, long id) { + // Insert desired behavior here. + Log.i(TAG, "Item clicked: " + id); + } + + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[] { + MainTable._ID, + MainTable.COLUMN_NAME_DATA, + }; + + public Loader onCreateLoader(int id, Bundle args) { + CursorLoader cl = new CursorLoader(getActivity(), MainTable.CONTENT_URI, + PROJECTION, null, null, null); + cl.setUpdateThrottle(2000); // update at most every 2 seconds. + return cl; + } + + public void onLoadFinished(Loader loader, Cursor data) { + mAdapter.swapCursor(data); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + public void onLoaderReset(Loader loader) { + mAdapter.swapCursor(null); + } + } +} + diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/SendResult.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/SendResult.java new file mode 100644 index 000000000..9345f5c19 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/app/SendResult.java @@ -0,0 +1,81 @@ +/* + * 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.sample.demos.app; + +// Need the following import to get access to the app resources, since this +// class is in a sub-package. +import com.actionbarsherlock.sample.demos.R; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; + + +/** + * Example of receiving a result from another activity. + */ +public class SendResult extends Activity +{ + /** + * Initialization of the Activity after it is first created. Must at least + * call {@link android.app.Activity#setContentView setContentView()} to + * describe what is to be displayed in the screen. + */ + @Override + protected void onCreate(Bundle savedInstanceState) + { + // Be sure to call the super class. + super.onCreate(savedInstanceState); + + // See assets/res/any/layout/hello_world.xml for this + // view layout definition, which is being set here as + // the content of our screen. + setContentView(R.layout.send_result); + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.corky); + button.setOnClickListener(mCorkyListener); + button = (Button)findViewById(R.id.violet); + button.setOnClickListener(mVioletListener); + } + + private OnClickListener mCorkyListener = new OnClickListener() + { + public void onClick(View v) + { + // To send a result, simply call setResult() before your + // activity is finished. + setResult(RESULT_OK, (new Intent()).setAction("Corky!")); + finish(); + } + }; + + private OnClickListener mVioletListener = new OnClickListener() + { + public void onClick(View v) + { + // To send a result, simply call setResult() before your + // activity is finished. + setResult(RESULT_OK, (new Intent()).setAction("Violet!")); + finish(); + } + }; +} + diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/content/LocalServiceBroadcaster.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/content/LocalServiceBroadcaster.java new file mode 100644 index 000000000..eace84d69 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/content/LocalServiceBroadcaster.java @@ -0,0 +1,167 @@ +/* + * 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.sample.demos.content; + + +import com.actionbarsherlock.sample.demos.R; + +import android.app.Activity; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.support.v4.content.LocalBroadcastManager; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.TextView; + +/** + * Demonstrates the use of a LocalBroadcastManager to easily communicate + * data from a service to any other interested code. + */ +public class LocalServiceBroadcaster extends Activity { + static final String ACTION_STARTED = "com.actionbarsherlock.sample.demos.STARTED"; + static final String ACTION_UPDATE = "com.actionbarsherlock.sample.demos.UPDATE"; + static final String ACTION_STOPPED = "com.actionbarsherlock.sample.demos.STOPPED"; + + LocalBroadcastManager mLocalBroadcastManager; + BroadcastReceiver mReceiver; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.local_service_broadcaster); + + // This is where we print the data we get back. + final TextView callbackData = (TextView)findViewById(R.id.callback); + + // Put in some initial text. + callbackData.setText("No broadcast received yet"); + + // We use this to send broadcasts within our local process. + mLocalBroadcastManager = LocalBroadcastManager.getInstance(this); + + // We are going to watch for interesting local broadcasts. + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_STARTED); + filter.addAction(ACTION_UPDATE); + filter.addAction(ACTION_STOPPED); + mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(ACTION_STARTED)) { + callbackData.setText("STARTED"); + } else if (intent.getAction().equals(ACTION_UPDATE)) { + callbackData.setText("Got update: " + intent.getIntExtra("value", 0)); + } else if (intent.getAction().equals(ACTION_STOPPED)) { + callbackData.setText("STOPPED"); + } + } + }; + mLocalBroadcastManager.registerReceiver(mReceiver, filter); + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.start); + button.setOnClickListener(mStartListener); + button = (Button)findViewById(R.id.stop); + button.setOnClickListener(mStopListener); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mLocalBroadcastManager.unregisterReceiver(mReceiver); + } + + private OnClickListener mStartListener = new OnClickListener() { + public void onClick(View v) { + startService(new Intent(LocalServiceBroadcaster.this, LocalService.class)); + } + }; + + private OnClickListener mStopListener = new OnClickListener() { + public void onClick(View v) { + stopService(new Intent(LocalServiceBroadcaster.this, LocalService.class)); + } + }; + + public static class LocalService extends Service { + LocalBroadcastManager mLocalBroadcastManager; + int mCurUpdate; + + static final int MSG_UPDATE = 1; + + Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE: { + mCurUpdate++; + Intent intent = new Intent(ACTION_UPDATE); + intent.putExtra("value", mCurUpdate); + mLocalBroadcastManager.sendBroadcast(intent); + Message nmsg = mHandler.obtainMessage(MSG_UPDATE); + mHandler.sendMessageDelayed(nmsg, 1000); + } break; + default: + super.handleMessage(msg); + } + } + }; + + @Override + public void onCreate() { + super.onCreate(); + mLocalBroadcastManager = LocalBroadcastManager.getInstance(this); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // Tell any local interested parties about the start. + mLocalBroadcastManager.sendBroadcast(new Intent(ACTION_STARTED)); + + // Prepare to do update reports. + mHandler.removeMessages(MSG_UPDATE); + Message msg = mHandler.obtainMessage(MSG_UPDATE); + mHandler.sendMessageDelayed(msg, 1000); + return Service.START_STICKY; + } + + @Override + public void onDestroy() { + super.onDestroy(); + + // Tell any local interested parties about the stop. + mLocalBroadcastManager.sendBroadcast(new Intent(ACTION_STOPPED)); + + // Stop doing updates. + mHandler.removeMessages(MSG_UPDATE); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + } +} diff --git a/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/view/CheckableFrameLayout.java b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/view/CheckableFrameLayout.java new file mode 100644 index 000000000..238773125 --- /dev/null +++ b/actionbarsherlock/samples/demos/src/com/actionbarsherlock/sample/demos/view/CheckableFrameLayout.java @@ -0,0 +1,49 @@ +/* + * 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.sample.demos.view; + +import android.content.Context; +import android.graphics.drawable.ColorDrawable; +import android.util.AttributeSet; +import android.widget.Checkable; +import android.widget.FrameLayout; + +public class CheckableFrameLayout extends FrameLayout implements Checkable { + private boolean mChecked; + + public CheckableFrameLayout(Context context) { + super(context); + } + + public CheckableFrameLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setChecked(boolean checked) { + mChecked = checked; + setBackgroundDrawable(checked ? new ColorDrawable(0xff0000a0) : null); + } + + public boolean isChecked() { + return mChecked; + } + + public void toggle() { + setChecked(!mChecked); + } + +} diff --git a/actionbarsherlock/samples/plugins/AndroidManifest.xml b/actionbarsherlock/samples/plugins/AndroidManifest.xml new file mode 100644 index 000000000..189e92769 --- /dev/null +++ b/actionbarsherlock/samples/plugins/AndroidManifest.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/plugins/README.md b/actionbarsherlock/samples/plugins/README.md new file mode 100644 index 000000000..9a223bb62 --- /dev/null +++ b/actionbarsherlock/samples/plugins/README.md @@ -0,0 +1,13 @@ +ActionBarSherlock Sample: Plugins +================================= + +See [actionbarsherlock.com/samples.html][1] for information on the sample +contained in this folder. + + + + + + + + [1]: http://actionbarsherlock.com/samples.html diff --git a/actionbarsherlock/samples/plugins/pom.xml b/actionbarsherlock/samples/plugins/pom.xml new file mode 100644 index 000000000..6a42052f0 --- /dev/null +++ b/actionbarsherlock/samples/plugins/pom.xml @@ -0,0 +1,121 @@ + + + + 4.0.0 + + com.actionbarsherlock + sample-plugins + Sample: Plugins + apk + + + com.actionbarsherlock + parent-sample + 3.4.2 + ../pom.xml + + + + + android + android + provided + + + + com.google.android.maps + maps + provided + + + + com.actionbarsherlock + library + ${project.version} + apklib + + + + com.actionbarsherlock + plugin-maps + ${project.version} + + + + com.actionbarsherlock + plugin-preference + ${project.version} + + + + + src + ${apk.prefix}-${project.artifactId}-unaligned + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + + + + + release + + + performRelease + true + + + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + true + + + ${project.build.directory}/${project.build.finalName}.apk + ${project.build.directory}/${apk.prefix}-${project.artifactId}.apk + + + + + alignApk + package + + zipalign + + + + + + + com.jakewharton + github-deploy-maven-plugin + 1.0.1 + + + aligned + + + + deploy + + + + + + + diff --git a/actionbarsherlock/samples/plugins/project.properties b/actionbarsherlock/samples/plugins/project.properties new file mode 100644 index 000000000..c6243b559 --- /dev/null +++ b/actionbarsherlock/samples/plugins/project.properties @@ -0,0 +1,14 @@ +# 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. + +# Project target. +target=Google Inc.:Google APIs:13 +android.library.reference.1=../../library +android.library.reference.2=../../plugins/maps +android.library.reference.3=../../plugins/preference diff --git a/actionbarsherlock/samples/plugins/res/drawable-hdpi/ic_compose.png b/actionbarsherlock/samples/plugins/res/drawable-hdpi/ic_compose.png new file mode 100644 index 000000000..cdb09e4c6 Binary files /dev/null and b/actionbarsherlock/samples/plugins/res/drawable-hdpi/ic_compose.png differ diff --git a/actionbarsherlock/samples/plugins/res/drawable-hdpi/ic_refresh.png b/actionbarsherlock/samples/plugins/res/drawable-hdpi/ic_refresh.png new file mode 100644 index 000000000..08c32e09e Binary files /dev/null and b/actionbarsherlock/samples/plugins/res/drawable-hdpi/ic_refresh.png differ diff --git a/actionbarsherlock/samples/plugins/res/drawable-hdpi/ic_search.png b/actionbarsherlock/samples/plugins/res/drawable-hdpi/ic_search.png new file mode 100644 index 000000000..59de344e7 Binary files /dev/null and b/actionbarsherlock/samples/plugins/res/drawable-hdpi/ic_search.png differ diff --git a/actionbarsherlock/samples/plugins/res/drawable-hdpi/icon.png b/actionbarsherlock/samples/plugins/res/drawable-hdpi/icon.png new file mode 100644 index 000000000..723fbf062 Binary files /dev/null and b/actionbarsherlock/samples/plugins/res/drawable-hdpi/icon.png differ diff --git a/actionbarsherlock/samples/plugins/res/drawable-ldpi/icon.png b/actionbarsherlock/samples/plugins/res/drawable-ldpi/icon.png new file mode 100644 index 000000000..181f679f5 Binary files /dev/null and b/actionbarsherlock/samples/plugins/res/drawable-ldpi/icon.png differ diff --git a/actionbarsherlock/samples/plugins/res/drawable-mdpi/ic_compose.png b/actionbarsherlock/samples/plugins/res/drawable-mdpi/ic_compose.png new file mode 100644 index 000000000..15a413053 Binary files /dev/null and b/actionbarsherlock/samples/plugins/res/drawable-mdpi/ic_compose.png differ diff --git a/actionbarsherlock/samples/plugins/res/drawable-mdpi/ic_refresh.png b/actionbarsherlock/samples/plugins/res/drawable-mdpi/ic_refresh.png new file mode 100644 index 000000000..55c43c327 Binary files /dev/null and b/actionbarsherlock/samples/plugins/res/drawable-mdpi/ic_refresh.png differ diff --git a/actionbarsherlock/samples/plugins/res/drawable-mdpi/ic_search.png b/actionbarsherlock/samples/plugins/res/drawable-mdpi/ic_search.png new file mode 100644 index 000000000..75339d906 Binary files /dev/null and b/actionbarsherlock/samples/plugins/res/drawable-mdpi/ic_search.png differ diff --git a/actionbarsherlock/samples/plugins/res/drawable-mdpi/icon.png b/actionbarsherlock/samples/plugins/res/drawable-mdpi/icon.png new file mode 100644 index 000000000..79f50d84c Binary files /dev/null and b/actionbarsherlock/samples/plugins/res/drawable-mdpi/icon.png differ diff --git a/actionbarsherlock/samples/plugins/res/drawable-xhdpi/icon.png b/actionbarsherlock/samples/plugins/res/drawable-xhdpi/icon.png new file mode 100644 index 000000000..34ca9a489 Binary files /dev/null and b/actionbarsherlock/samples/plugins/res/drawable-xhdpi/icon.png differ diff --git a/actionbarsherlock/samples/plugins/res/layout/actionbar_preferenceactivity.xml b/actionbarsherlock/samples/plugins/res/layout/actionbar_preferenceactivity.xml new file mode 100644 index 000000000..d2e1e83d7 --- /dev/null +++ b/actionbarsherlock/samples/plugins/res/layout/actionbar_preferenceactivity.xml @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/plugins/res/layout/map_simple.xml b/actionbarsherlock/samples/plugins/res/layout/map_simple.xml new file mode 100644 index 000000000..c91ec5f95 --- /dev/null +++ b/actionbarsherlock/samples/plugins/res/layout/map_simple.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/plugins/res/values/arrays.xml b/actionbarsherlock/samples/plugins/res/values/arrays.xml new file mode 100644 index 000000000..e511636c0 --- /dev/null +++ b/actionbarsherlock/samples/plugins/res/values/arrays.xml @@ -0,0 +1,16 @@ + + + + + Alpha Option 01 + Beta Option 02 + Charlie Option 03 + + + + + alpha + beta + charlie + + diff --git a/actionbarsherlock/samples/plugins/res/values/strings.xml b/actionbarsherlock/samples/plugins/res/values/strings.xml new file mode 100644 index 000000000..52db11c1a --- /dev/null +++ b/actionbarsherlock/samples/plugins/res/values/strings.xml @@ -0,0 +1,76 @@ + + + + + + + 0GPE4Lb_ZG_rnk3YyJHd7pBAxP0BPBJvmZR_-Kw + + ABS: Plugins Sample + + Preference/Simple + You can use the `SherlockPreferenceActivity` base class to place the action bar on top of a normal `PreferenceActivity`.\n\nThis does not yet support the use of `PreferenceFragments`, however. + + If you compiled this application yourself you need to specify your own API key in res/values/strings.xml + Map/Simple Activity + Map/Simple Fragment + Map/Activity (with Action Bar) + Map/Fragment (with Action Bar) + + In-line preferences + Dialog-based preferences + Launch preferences + Preference attributes + + Checkbox preference + This is a checkbox + + Edit text preference + An example that uses an edit text dialog + Enter your favorite animal + + List preference + An example that uses a list dialog + Choose one + + Screen preference + Shows another screen of preferences + + Toggle preference + Preference that is on the next screen but same hierarchy + + Intent preference + Launches an Activity from an Intent + + My preference + This is a custom counter preference + + Haunted preference + I\'m on! :) + I\'m off! :( + + Parent checkbox preference + This is visually a parent + Child checkbox preference + This is visually a child + + Example preference dependency + WiFi + WiFi settings + + beta + Default value + diff --git a/actionbarsherlock/samples/plugins/res/xml/preferences.xml b/actionbarsherlock/samples/plugins/res/xml/preferences.xml new file mode 100644 index 000000000..e84c54ae0 --- /dev/null +++ b/actionbarsherlock/samples/plugins/res/xml/preferences.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/Support4Demos.java b/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/Support4Demos.java new file mode 100644 index 000000000..2a25d60f4 --- /dev/null +++ b/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/Support4Demos.java @@ -0,0 +1,149 @@ +/* + * 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.sample.plugins; + +import android.app.ListActivity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.view.View; +import android.widget.ListView; +import android.widget.SimpleAdapter; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Support4Demos extends ListActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + String path = intent.getStringExtra("com.example.android.apis.Path"); + + if (path == null) { + path = ""; + } + + setListAdapter(new SimpleAdapter(this, getData(path), + android.R.layout.simple_list_item_1, new String[] { "title" }, + new int[] { android.R.id.text1 })); + getListView().setTextFilterEnabled(true); + } + + protected List> getData(String prefix) { + List> myData = new ArrayList>(); + + Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.addCategory("com.actionbarsherlock.sample.plugins.SUPPORT4_SAMPLE_CODE"); + + PackageManager pm = getPackageManager(); + List list = pm.queryIntentActivities(mainIntent, 0); + + if (null == list) + return myData; + + String[] prefixPath; + String prefixWithSlash = prefix; + + if (prefix.equals("")) { + prefixPath = null; + } else { + prefixPath = prefix.split("/"); + prefixWithSlash = prefix + "/"; + } + + int len = list.size(); + + Map entries = new HashMap(); + + for (int i = 0; i < len; i++) { + ResolveInfo info = list.get(i); + CharSequence labelSeq = info.loadLabel(pm); + String label = labelSeq != null + ? labelSeq.toString() + : info.activityInfo.name; + + if (prefixWithSlash.length() == 0 || label.startsWith(prefixWithSlash)) { + + String[] labelPath = label.split("/"); + + String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length]; + + if ((prefixPath != null ? prefixPath.length : 0) == labelPath.length - 1) { + addItem(myData, nextLabel, activityIntent( + info.activityInfo.applicationInfo.packageName, + info.activityInfo.name)); + } else { + if (entries.get(nextLabel) == null) { + addItem(myData, nextLabel, browseIntent(prefix.equals("") ? nextLabel : prefix + "/" + nextLabel)); + entries.put(nextLabel, true); + } + } + } + } + + Collections.sort(myData, sDisplayNameComparator); + + return myData; + } + + private final static Comparator> sDisplayNameComparator = + new Comparator>() { + private final Collator collator = Collator.getInstance(); + + public int compare(Map map1, Map map2) { + return collator.compare(map1.get("title"), map2.get("title")); + } + }; + + protected Intent activityIntent(String pkg, String componentName) { + Intent result = new Intent(); + result.setClassName(pkg, componentName); + return result; + } + + protected Intent browseIntent(String path) { + Intent result = new Intent(); + result.setClass(this, Support4Demos.class); + result.putExtra("com.example.android.apis.Path", path); + return result; + } + + protected void addItem(List> data, String name, Intent intent) { + Map temp = new HashMap(); + temp.put("title", name); + temp.put("intent", intent); + data.add(temp); + } + + @Override + @SuppressWarnings("unchecked") + protected void onListItemClick(ListView l, View v, int position, long id) { + Map map = (Map)l.getItemAtPosition(position); + + Intent intent = (Intent) map.get("intent"); + startActivity(intent); + } +} diff --git a/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/MapActivity.java b/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/MapActivity.java new file mode 100644 index 000000000..ba06fc716 --- /dev/null +++ b/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/MapActivity.java @@ -0,0 +1,26 @@ +package com.actionbarsherlock.sample.plugins.app; + +import com.actionbarsherlock.sample.plugins.R; +import com.google.android.maps.MapView; +import android.os.Bundle; +import android.support.v4.app.FragmentMapActivity; +import android.widget.Toast; + +public class MapActivity extends FragmentMapActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //MapView for the lose :( + Toast.makeText(this, R.string.map_warning, Toast.LENGTH_LONG).show(); + + setContentView(R.layout.map_simple); + MapView mapView = (MapView)findViewById(R.id.mapview); + mapView.setBuiltInZoomControls(true); + } + + @Override + protected boolean isRouteDisplayed() { + return false; + } +} diff --git a/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/MapActivityActionBar.java b/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/MapActivityActionBar.java new file mode 100644 index 000000000..0537614b9 --- /dev/null +++ b/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/MapActivityActionBar.java @@ -0,0 +1,45 @@ +package com.actionbarsherlock.sample.plugins.app; + +import com.actionbarsherlock.sample.plugins.R; +import com.google.android.maps.MapView; +import android.os.Bundle; +import android.support.v4.app.FragmentMapActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.widget.Toast; + +public class MapActivityActionBar extends FragmentMapActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //MapView for the lose :( + Toast.makeText(this, R.string.map_warning, Toast.LENGTH_LONG).show(); + + setContentView(R.layout.map_simple); + MapView mapView = (MapView)findViewById(R.id.mapview); + mapView.setBuiltInZoomControls(true); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add("Save") + .setIcon(R.drawable.ic_compose) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + menu.add("Search") + .setIcon(R.drawable.ic_search) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + menu.add("Refresh") + .setIcon(R.drawable.ic_refresh) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + return super.onCreateOptionsMenu(menu); + } + + @Override + protected boolean isRouteDisplayed() { + return false; + } +} diff --git a/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/MapFragment.java b/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/MapFragment.java new file mode 100644 index 000000000..0d02aa93f --- /dev/null +++ b/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/MapFragment.java @@ -0,0 +1,43 @@ +package com.actionbarsherlock.sample.plugins.app; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentMapActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.actionbarsherlock.sample.plugins.R; +import com.google.android.maps.MapView; + +public class MapFragment extends FragmentMapActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //MapView for the lose :( + Toast.makeText(this, R.string.map_warning, Toast.LENGTH_LONG).show(); + + getSupportFragmentManager() + .beginTransaction() + .add(android.R.id.content, new MyMapFragment()) + .commit(); + } + + public static final class MyMapFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.map_simple, container, false); + MapView mapView = (MapView)view.findViewById(R.id.mapview); + mapView.setBuiltInZoomControls(true); + return view; + } + } + + @Override + protected boolean isRouteDisplayed() { + return false; + } +} diff --git a/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/MapFragmentActionBar.java b/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/MapFragmentActionBar.java new file mode 100644 index 000000000..3134b2ebd --- /dev/null +++ b/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/MapFragmentActionBar.java @@ -0,0 +1,62 @@ +package com.actionbarsherlock.sample.plugins.app; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentMapActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.actionbarsherlock.sample.plugins.R; +import com.google.android.maps.MapView; + +public class MapFragmentActionBar extends FragmentMapActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //MapView for the lose :( + Toast.makeText(this, R.string.map_warning, Toast.LENGTH_LONG).show(); + + getSupportFragmentManager() + .beginTransaction() + .add(android.R.id.content, new MyMapFragment()) + .commit(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add("Save") + .setIcon(R.drawable.ic_compose) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + menu.add("Search") + .setIcon(R.drawable.ic_search) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + menu.add("Refresh") + .setIcon(R.drawable.ic_refresh) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + return super.onCreateOptionsMenu(menu); + } + + public static final class MyMapFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.map_simple, container, false); + MapView mapView = (MapView)view.findViewById(R.id.mapview); + mapView.setBuiltInZoomControls(true); + return view; + } + } + + @Override + protected boolean isRouteDisplayed() { + return false; + } +} diff --git a/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/PreferenceSimple.java b/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/PreferenceSimple.java new file mode 100644 index 000000000..5602dfae8 --- /dev/null +++ b/actionbarsherlock/samples/plugins/src/com/actionbarsherlock/sample/plugins/app/PreferenceSimple.java @@ -0,0 +1,49 @@ +/* + * 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.sample.plugins.app; + +import android.os.Bundle; +import android.support.v4.app.SherlockPreferenceActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; + +import com.actionbarsherlock.sample.plugins.R; + +public class PreferenceSimple extends SherlockPreferenceActivity { + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add("Save") + .setIcon(R.drawable.ic_compose) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + menu.add("Search") + .setIcon(R.drawable.ic_search) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + menu.add("Refresh") + .setIcon(R.drawable.ic_refresh) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + return super.onCreateOptionsMenu(menu); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.preferences); + } +} diff --git a/actionbarsherlock/samples/pom.xml b/actionbarsherlock/samples/pom.xml new file mode 100644 index 000000000..9ddb6da3e --- /dev/null +++ b/actionbarsherlock/samples/pom.xml @@ -0,0 +1,24 @@ + + + + 4.0.0 + + com.actionbarsherlock + parent-sample + Sample (Parent) + pom + + + com.actionbarsherlock + parent + 3.4.2 + ../pom.xml + + + + demos + plugins + shakespeare + styled + + diff --git a/actionbarsherlock/samples/shakespeare/AndroidManifest.xml b/actionbarsherlock/samples/shakespeare/AndroidManifest.xml new file mode 100644 index 000000000..69fa3bcb9 --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/shakespeare/README.md b/actionbarsherlock/samples/shakespeare/README.md new file mode 100644 index 000000000..5e507379c --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/README.md @@ -0,0 +1,13 @@ +ActionBarSherlock Sample: Shakespeare +===================================== + +See [actionbarsherlock.com/samples.html][1] for information on the sample +contained in this folder. + + + + + + + + [1]: http://actionbarsherlock.com/samples.html diff --git a/actionbarsherlock/samples/shakespeare/pom.xml b/actionbarsherlock/samples/shakespeare/pom.xml new file mode 100644 index 000000000..9ffb7b5a9 --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/pom.xml @@ -0,0 +1,103 @@ + + + + 4.0.0 + + com.actionbarsherlock + sample-shakespeare + Sample: Shakespeare + apk + + + com.actionbarsherlock + parent-sample + 3.4.2 + ../pom.xml + + + + + android + android + provided + + + + com.actionbarsherlock + library + ${project.version} + apklib + + + + + src + ${apk.prefix}-${project.artifactId}-unaligned + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + + + + + release + + + performRelease + true + + + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + true + + + ${project.build.directory}/${project.build.finalName}.apk + ${project.build.directory}/${apk.prefix}-${project.artifactId}.apk + + + + + alignApk + package + + zipalign + + + + + + + com.jakewharton + github-deploy-maven-plugin + 1.0.1 + + + aligned + + + + deploy + + + + + + + diff --git a/actionbarsherlock/samples/shakespeare/project.properties b/actionbarsherlock/samples/shakespeare/project.properties new file mode 100644 index 000000000..34f549f8c --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/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. + +# Project target. +target=android-13 +android.library.reference.1=../../library diff --git a/actionbarsherlock/samples/shakespeare/res/drawable-hdpi/icon.png b/actionbarsherlock/samples/shakespeare/res/drawable-hdpi/icon.png new file mode 100644 index 000000000..723fbf062 Binary files /dev/null and b/actionbarsherlock/samples/shakespeare/res/drawable-hdpi/icon.png differ diff --git a/actionbarsherlock/samples/shakespeare/res/drawable-ldpi/icon.png b/actionbarsherlock/samples/shakespeare/res/drawable-ldpi/icon.png new file mode 100644 index 000000000..181f679f5 Binary files /dev/null and b/actionbarsherlock/samples/shakespeare/res/drawable-ldpi/icon.png differ diff --git a/actionbarsherlock/samples/shakespeare/res/drawable-mdpi/icon.png b/actionbarsherlock/samples/shakespeare/res/drawable-mdpi/icon.png new file mode 100644 index 000000000..79f50d84c Binary files /dev/null and b/actionbarsherlock/samples/shakespeare/res/drawable-mdpi/icon.png differ diff --git a/actionbarsherlock/samples/shakespeare/res/drawable-xhdpi/icon.png b/actionbarsherlock/samples/shakespeare/res/drawable-xhdpi/icon.png new file mode 100644 index 000000000..34ca9a489 Binary files /dev/null and b/actionbarsherlock/samples/shakespeare/res/drawable-xhdpi/icon.png differ diff --git a/actionbarsherlock/samples/shakespeare/res/layout-land/activity_titles.xml b/actionbarsherlock/samples/shakespeare/res/layout-land/activity_titles.xml new file mode 100644 index 000000000..cbacb3b2c --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/res/layout-land/activity_titles.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/shakespeare/res/layout-xlarge-land/activity_titles.xml b/actionbarsherlock/samples/shakespeare/res/layout-xlarge-land/activity_titles.xml new file mode 100644 index 000000000..2010ecf2d --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/res/layout-xlarge-land/activity_titles.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/shakespeare/res/layout/activity_titles.xml b/actionbarsherlock/samples/shakespeare/res/layout/activity_titles.xml new file mode 100644 index 000000000..3c6146a46 --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/res/layout/activity_titles.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/shakespeare/res/values/strings.xml b/actionbarsherlock/samples/shakespeare/res/values/strings.xml new file mode 100644 index 000000000..97871a1da --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/res/values/strings.xml @@ -0,0 +1,7 @@ + + + ABS: Shakespeare Sample + + Shakespeare Titles + Shakespeare Details + diff --git a/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/Shakespeare.java b/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/Shakespeare.java new file mode 100644 index 000000000..de3e59d8b --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/Shakespeare.java @@ -0,0 +1,223 @@ +package com.actionbarsherlock.sample.shakespeare; + +public final class Shakespeare { + /** + * Our data, part 1. + */ + public static final String[] TITLES = + { + "Henry IV (1)", + "Henry V", + "Henry VIII", + "Richard II", + "Richard III", + "Merchant of Venice", + "Othello", + "King Lear" + }; + + /** + * Our data, part 2. + */ + public static final String[] DIALOGUE = + { + "So shaken as we are, so wan with care," + + "Find we a time for frighted peace to pant," + + "And breathe short-winded accents of new broils" + + "To be commenced in strands afar remote." + + "No more the thirsty entrance of this soil" + + "Shall daub her lips with her own children's blood;" + + "Nor more shall trenching war channel her fields," + + "Nor bruise her flowerets with the armed hoofs" + + "Of hostile paces: those opposed eyes," + + "Which, like the meteors of a troubled heaven," + + "All of one nature, of one substance bred," + + "Did lately meet in the intestine shock" + + "And furious close of civil butchery" + + "Shall now, in mutual well-beseeming ranks," + + "March all one way and be no more opposed" + + "Against acquaintance, kindred and allies:" + + "The edge of war, like an ill-sheathed knife," + + "No more shall cut his master. Therefore, friends," + + "As far as to the sepulchre of Christ," + + "Whose soldier now, under whose blessed cross" + + "We are impressed and engaged to fight," + + "Forthwith a power of English shall we levy;" + + "Whose arms were moulded in their mothers' womb" + + "To chase these pagans in those holy fields" + + "Over whose acres walk'd those blessed feet" + + "Which fourteen hundred years ago were nail'd" + + "For our advantage on the bitter cross." + + "But this our purpose now is twelve month old," + + "And bootless 'tis to tell you we will go:" + + "Therefore we meet not now. Then let me hear" + + "Of you, my gentle cousin Westmoreland," + + "What yesternight our council did decree" + + "In forwarding this dear expedience.", + + "Hear him but reason in divinity," + + "And all-admiring with an inward wish" + + "You would desire the king were made a prelate:" + + "Hear him debate of commonwealth affairs," + + "You would say it hath been all in all his study:" + + "List his discourse of war, and you shall hear" + + "A fearful battle render'd you in music:" + + "Turn him to any cause of policy," + + "The Gordian knot of it he will unloose," + + "Familiar as his garter: that, when he speaks," + + "The air, a charter'd libertine, is still," + + "And the mute wonder lurketh in men's ears," + + "To steal his sweet and honey'd sentences;" + + "So that the art and practic part of life" + + "Must be the mistress to this theoric:" + + "Which is a wonder how his grace should glean it," + + "Since his addiction was to courses vain," + + "His companies unletter'd, rude and shallow," + + "His hours fill'd up with riots, banquets, sports," + + "And never noted in him any study," + + "Any retirement, any sequestration" + + "From open haunts and popularity.", + + "I come no more to make you laugh: things now," + + "That bear a weighty and a serious brow," + + "Sad, high, and working, full of state and woe," + + "Such noble scenes as draw the eye to flow," + + "We now present. Those that can pity, here" + + "May, if they think it well, let fall a tear;" + + "The subject will deserve it. Such as give" + + "Their money out of hope they may believe," + + "May here find truth too. Those that come to see" + + "Only a show or two, and so agree" + + "The play may pass, if they be still and willing," + + "I'll undertake may see away their shilling" + + "Richly in two short hours. Only they" + + "That come to hear a merry bawdy play," + + "A noise of targets, or to see a fellow" + + "In a long motley coat guarded with yellow," + + "Will be deceived; for, gentle hearers, know," + + "To rank our chosen truth with such a show" + + "As fool and fight is, beside forfeiting" + + "Our own brains, and the opinion that we bring," + + "To make that only true we now intend," + + "Will leave us never an understanding friend." + + "Therefore, for goodness' sake, and as you are known" + + "The first and happiest hearers of the town," + + "Be sad, as we would make ye: think ye see" + + "The very persons of our noble story" + + "As they were living; think you see them great," + + "And follow'd with the general throng and sweat" + + "Of thousand friends; then in a moment, see" + + "How soon this mightiness meets misery:" + + "And, if you can be merry then, I'll say" + + "A man may weep upon his wedding-day.", + + "First, heaven be the record to my speech!" + + "In the devotion of a subject's love," + + "Tendering the precious safety of my prince," + + "And free from other misbegotten hate," + + "Come I appellant to this princely presence." + + "Now, Thomas Mowbray, do I turn to thee," + + "And mark my greeting well; for what I speak" + + "My body shall make good upon this earth," + + "Or my divine soul answer it in heaven." + + "Thou art a traitor and a miscreant," + + "Too good to be so and too bad to live," + + "Since the more fair and crystal is the sky," + + "The uglier seem the clouds that in it fly." + + "Once more, the more to aggravate the note," + + "With a foul traitor's name stuff I thy throat;" + + "And wish, so please my sovereign, ere I move," + + "What my tongue speaks my right drawn sword may prove.", + + "Now is the winter of our discontent" + + "Made glorious summer by this sun of York;" + + "And all the clouds that lour'd upon our house" + + "In the deep bosom of the ocean buried." + + "Now are our brows bound with victorious wreaths;" + + "Our bruised arms hung up for monuments;" + + "Our stern alarums changed to merry meetings," + + "Our dreadful marches to delightful measures." + + "Grim-visaged war hath smooth'd his wrinkled front;" + + "And now, instead of mounting barded steeds" + + "To fright the souls of fearful adversaries," + + "He capers nimbly in a lady's chamber" + + "To the lascivious pleasing of a lute." + + "But I, that am not shaped for sportive tricks," + + "Nor made to court an amorous looking-glass;" + + "I, that am rudely stamp'd, and want love's majesty" + + "To strut before a wanton ambling nymph;" + + "I, that am curtail'd of this fair proportion," + + "Cheated of feature by dissembling nature," + + "Deformed, unfinish'd, sent before my time" + + "Into this breathing world, scarce half made up," + + "And that so lamely and unfashionable" + + "That dogs bark at me as I halt by them;" + + "Why, I, in this weak piping time of peace," + + "Have no delight to pass away the time," + + "Unless to spy my shadow in the sun" + + "And descant on mine own deformity:" + + "And therefore, since I cannot prove a lover," + + "To entertain these fair well-spoken days," + + "I am determined to prove a villain" + + "And hate the idle pleasures of these days." + + "Plots have I laid, inductions dangerous," + + "By drunken prophecies, libels and dreams," + + "To set my brother Clarence and the king" + + "In deadly hate the one against the other:" + + "And if King Edward be as true and just" + + "As I am subtle, false and treacherous," + + "This day should Clarence closely be mew'd up," + + "About a prophecy, which says that 'G'" + + "Of Edward's heirs the murderer shall be." + + "Dive, thoughts, down to my soul: here" + + "Clarence comes.", + + "To bait fish withal: if it will feed nothing else," + + "it will feed my revenge. He hath disgraced me, and" + + "hindered me half a million; laughed at my losses," + + "mocked at my gains, scorned my nation, thwarted my" + + "bargains, cooled my friends, heated mine" + + "enemies; and what's his reason? I am a Jew. Hath" + + "not a Jew eyes? hath not a Jew hands, organs," + + "dimensions, senses, affections, passions? fed with" + + "the same food, hurt with the same weapons, subject" + + "to the same diseases, healed by the same means," + + "warmed and cooled by the same winter and summer, as" + + "a Christian is? If you prick us, do we not bleed?" + + "if you tickle us, do we not laugh? if you poison" + + "us, do we not die? and if you wrong us, shall we not" + + "revenge? If we are like you in the rest, we will" + + "resemble you in that. If a Jew wrong a Christian," + + "what is his humility? Revenge. If a Christian" + + "wrong a Jew, what should his sufferance be by" + + "Christian example? Why, revenge. The villany you" + + "teach me, I will execute, and it shall go hard but I" + + "will better the instruction.", + + "Virtue! a fig! 'tis in ourselves that we are thus" + + "or thus. Our bodies are our gardens, to the which" + + "our wills are gardeners: so that if we will plant" + + "nettles, or sow lettuce, set hyssop and weed up" + + "thyme, supply it with one gender of herbs, or" + + "distract it with many, either to have it sterile" + + "with idleness, or manured with industry, why, the" + + "power and corrigible authority of this lies in our" + + "wills. If the balance of our lives had not one" + + "scale of reason to poise another of sensuality, the" + + "blood and baseness of our natures would conduct us" + + "to most preposterous conclusions: but we have" + + "reason to cool our raging motions, our carnal" + + "stings, our unbitted lusts, whereof I take this that" + + "you call love to be a sect or scion.", + + "Blow, winds, and crack your cheeks! rage! blow!" + + "You cataracts and hurricanoes, spout" + + "Till you have drench'd our steeples, drown'd the cocks!" + + "You sulphurous and thought-executing fires," + + "Vaunt-couriers to oak-cleaving thunderbolts," + + "Singe my white head! And thou, all-shaking thunder," + + "Smite flat the thick rotundity o' the world!" + + "Crack nature's moulds, an germens spill at once," + + "That make ingrateful man!" + }; +} diff --git a/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/activities/DetailsActivity.java b/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/activities/DetailsActivity.java new file mode 100644 index 000000000..f9452aee8 --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/activities/DetailsActivity.java @@ -0,0 +1,60 @@ +package com.actionbarsherlock.sample.shakespeare.activities; + +import android.app.Activity; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.MenuItem; +import com.actionbarsherlock.sample.shakespeare.fragments.DetailsFragment; + +public class DetailsActivity extends FragmentActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { + // If the screen is now in landscape mode, we can show the + // dialog in-line with the list so we don't need this activity. + finish(); + return; + } + + if (savedInstanceState == null) { + // During initial setup, plug in the details fragment. + DetailsFragment details = new DetailsFragment(); + details.setArguments(getIntent().getExtras()); + + getSupportFragmentManager() + .beginTransaction() + .add(android.R.id.content, details) + .commit(); + } + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + Intent intent = new Intent(this, TitlesActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + + //Get rid of the slide-in animation, if possible + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) { + OverridePendingTransition.invoke(this); + } + } + + return super.onOptionsItemSelected(item); + } + + private static final class OverridePendingTransition { + static void invoke(Activity activity) { + activity.overridePendingTransition(0, 0); + } + } +} diff --git a/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/activities/TitlesActivity.java b/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/activities/TitlesActivity.java new file mode 100644 index 000000000..e23822cd7 --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/activities/TitlesActivity.java @@ -0,0 +1,14 @@ +package com.actionbarsherlock.sample.shakespeare.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import com.actionbarsherlock.sample.shakespeare.R; + +public class TitlesActivity extends FragmentActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setTitle(R.string.activity_titles); + setContentView(R.layout.activity_titles); + } +} diff --git a/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/fragments/DetailsFragment.java b/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/fragments/DetailsFragment.java new file mode 100644 index 000000000..2f2dcd69b --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/fragments/DetailsFragment.java @@ -0,0 +1,50 @@ +package com.actionbarsherlock.sample.shakespeare.fragments; + +import com.actionbarsherlock.sample.shakespeare.Shakespeare; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; +import android.widget.TextView; + +public class DetailsFragment extends Fragment { + /** + * Create a new instance of DetailsFragment, initialized to + * show the text at 'index'. + */ + public static DetailsFragment newInstance(int index) { + // Supply index input as an argument. + Bundle args = new Bundle(); + args.putInt("index", index); + + DetailsFragment f = new DetailsFragment(); + f.setArguments(args); + + return f; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + if (container == null) { + // We have different layouts, and in one of them this + // fragment's containing frame doesn't exist. The fragment + // may still be created from its saved state, but there is + // no reason to try to create its view hierarchy because it + // won't be displayed. Note this is not needed -- we could + // just run the code below, where we would create and return + // the view hierarchy; it would just never be used. + return null; + } + + ScrollView scroller = new ScrollView(getActivity()); + TextView text = new TextView(getActivity()); + int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getActivity().getResources().getDisplayMetrics()); + text.setPadding(padding, padding, padding, padding); + scroller.addView(text); + text.setText(Shakespeare.DIALOGUE[getArguments().getInt("index", 0)]); + return scroller; + } +} diff --git a/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/fragments/TitlesFragment.java b/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/fragments/TitlesFragment.java new file mode 100644 index 000000000..5555af84c --- /dev/null +++ b/actionbarsherlock/samples/shakespeare/src/com/actionbarsherlock/sample/shakespeare/fragments/TitlesFragment.java @@ -0,0 +1,96 @@ +package com.actionbarsherlock.sample.shakespeare.fragments; + +import com.actionbarsherlock.sample.shakespeare.R; +import com.actionbarsherlock.sample.shakespeare.Shakespeare; +import com.actionbarsherlock.sample.shakespeare.activities.DetailsActivity; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.app.ListFragment; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +public class TitlesFragment extends ListFragment { + boolean mHasDetailsFrame; + int mPositionChecked = 0; + int mPositionShown = -1; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Populate list with our static array of titles. + setListAdapter(new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, Shakespeare.TITLES)); + + // Check to see if we have a frame in which to embed the details + // fragment directly in the containing UI. + View detailsFrame = getActivity().findViewById(R.id.frame_details); + mHasDetailsFrame = (detailsFrame != null) && (detailsFrame.getVisibility() == View.VISIBLE); + + if (savedInstanceState != null) { + // Restore last state for checked position. + mPositionChecked = savedInstanceState.getInt("curChoice", 0); + mPositionShown = savedInstanceState.getInt("shownChoice", -1); + } + + if (mHasDetailsFrame) { + // In dual-pane mode, the list view highlights the selected item. + getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); + // Make sure our UI is in the correct state. + showDetails(mPositionChecked); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putInt("curChoice", mPositionChecked); + outState.putInt("shownChoice", mPositionShown); + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + showDetails(position); + } + + /** + * Helper function to show the details of a selected item, either by + * displaying a fragment in-place in the current UI, or starting a + * whole new activity in which it is displayed. + */ + void showDetails(int index) { + mPositionChecked = index; + + if (mHasDetailsFrame) { + // We can display everything in-place with fragments, so update + // the list to highlight the selected item and show the data. + getListView().setItemChecked(index, true); + + if (mPositionShown != mPositionChecked) { + // If we are not currently showing a fragment for the new + // position, we need to create and install a new one. + DetailsFragment df = DetailsFragment.newInstance(index); + + // Execute a transaction, replacing any existing fragment + // with this one inside the frame. + getFragmentManager() + .beginTransaction() + .replace(R.id.frame_details, df) + .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) + .commit(); + + mPositionShown = index; + } + + } else { + // Otherwise we need to launch a new activity to display + // the dialog fragment with selected text. + Intent intent = new Intent(); + intent.setClass(getActivity(), DetailsActivity.class); + intent.putExtra("index", index); + startActivity(intent); + } + } +} diff --git a/actionbarsherlock/samples/styled/AndroidManifest.xml b/actionbarsherlock/samples/styled/AndroidManifest.xml new file mode 100644 index 000000000..f5b398484 --- /dev/null +++ b/actionbarsherlock/samples/styled/AndroidManifest.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/styled/README.md b/actionbarsherlock/samples/styled/README.md new file mode 100644 index 000000000..6323b1d51 --- /dev/null +++ b/actionbarsherlock/samples/styled/README.md @@ -0,0 +1,13 @@ +ActionBarSherlock Sample: Styled +================================ + +See [actionbarsherlock.com/samples.html][1] for information on the sample +contained in this folder. + + + + + + + + [1]: http://actionbarsherlock.com/samples.html diff --git a/actionbarsherlock/samples/styled/pom.xml b/actionbarsherlock/samples/styled/pom.xml new file mode 100644 index 000000000..4aa2aff59 --- /dev/null +++ b/actionbarsherlock/samples/styled/pom.xml @@ -0,0 +1,103 @@ + + + + 4.0.0 + + com.actionbarsherlock + sample-styled + Sample: Styled + apk + + + com.actionbarsherlock + parent-sample + 3.4.2 + ../pom.xml + + + + + android + android + provided + + + + com.actionbarsherlock + library + ${project.version} + apklib + + + + + src + ${apk.prefix}-${project.artifactId}-unaligned + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + + + + + release + + + performRelease + true + + + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + true + + + ${project.build.directory}/${project.build.finalName}.apk + ${project.build.directory}/${apk.prefix}-${project.artifactId}.apk + + + + + alignApk + package + + zipalign + + + + + + + com.jakewharton + github-deploy-maven-plugin + 1.0.1 + + + aligned + + + + deploy + + + + + + + diff --git a/actionbarsherlock/samples/styled/project.properties b/actionbarsherlock/samples/styled/project.properties new file mode 100644 index 000000000..34f549f8c --- /dev/null +++ b/actionbarsherlock/samples/styled/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. + +# Project target. +target=android-13 +android.library.reference.1=../../library diff --git a/actionbarsherlock/samples/styled/res/drawable-hdpi-v9/ic_stat_android.png b/actionbarsherlock/samples/styled/res/drawable-hdpi-v9/ic_stat_android.png new file mode 100644 index 000000000..0d0e37f81 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-hdpi-v9/ic_stat_android.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-hdpi/ic_stat_android.png b/actionbarsherlock/samples/styled/res/drawable-hdpi/ic_stat_android.png new file mode 100644 index 000000000..eb01dd014 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-hdpi/ic_stat_android.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-hdpi/icon.png b/actionbarsherlock/samples/styled/res/drawable-hdpi/icon.png new file mode 100644 index 000000000..723fbf062 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-hdpi/icon.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-ldpi-v9/ic_stat_android.png b/actionbarsherlock/samples/styled/res/drawable-ldpi-v9/ic_stat_android.png new file mode 100644 index 000000000..a2aa04f03 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-ldpi-v9/ic_stat_android.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-ldpi/ic_stat_android.png b/actionbarsherlock/samples/styled/res/drawable-ldpi/ic_stat_android.png new file mode 100644 index 000000000..ecfc1f62c Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-ldpi/ic_stat_android.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-ldpi/icon.png b/actionbarsherlock/samples/styled/res/drawable-ldpi/icon.png new file mode 100644 index 000000000..181f679f5 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-ldpi/icon.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi-v9/ic_stat_android.png b/actionbarsherlock/samples/styled/res/drawable-mdpi-v9/ic_stat_android.png new file mode 100644 index 000000000..3e71d610d Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi-v9/ic_stat_android.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_btn_check_off_pressed_holo_light.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_btn_check_off_pressed_holo_light.png new file mode 100644 index 000000000..9ca4c6070 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_btn_check_off_pressed_holo_light.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_btn_check_on_pressed_holo_light.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_btn_check_on_pressed_holo_light.png new file mode 100644 index 000000000..45f69c31c Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_btn_check_on_pressed_holo_light.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_btn_radio_off_pressed_holo_light.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_btn_radio_off_pressed_holo_light.png new file mode 100644 index 000000000..f4393fce7 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_btn_radio_off_pressed_holo_light.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_btn_radio_on_pressed_holo_light.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_btn_radio_on_pressed_holo_light.png new file mode 100644 index 000000000..2f54feb69 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_btn_radio_on_pressed_holo_light.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_logo.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_logo.png new file mode 100644 index 000000000..0d8f2c9dc Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_logo.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_menu_dropdown_panel_holo_light.9.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_menu_dropdown_panel_holo_light.9.png new file mode 100644 index 000000000..421b99be4 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_menu_dropdown_panel_holo_light.9.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_spinner_focused_holo_light.9.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_spinner_focused_holo_light.9.png new file mode 100644 index 000000000..5bbbf6395 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_spinner_focused_holo_light.9.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_spinner_pressed_holo_light.9.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_spinner_pressed_holo_light.9.png new file mode 100644 index 000000000..f95e6d2e1 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_spinner_pressed_holo_light.9.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_tab_selected_holo.9.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_tab_selected_holo.9.png new file mode 100644 index 000000000..14cdbd0ef Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_tab_selected_holo.9.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_tab_selected_pressed_holo.9.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_tab_selected_pressed_holo.9.png new file mode 100644 index 000000000..04d3f6cec Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_tab_selected_pressed_holo.9.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_tab_unselected_holo.9.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_tab_unselected_holo.9.png new file mode 100644 index 000000000..458214c68 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ad_tab_unselected_holo.9.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/btn_check_off_holo_light.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/btn_check_off_holo_light.png new file mode 100644 index 000000000..a80c349db Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/btn_check_off_holo_light.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/btn_check_on_holo_light.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/btn_check_on_holo_light.png new file mode 100644 index 000000000..8fdb3429a Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/btn_check_on_holo_light.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/btn_radio_off_holo_light.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/btn_radio_off_holo_light.png new file mode 100644 index 000000000..c67e9fbb9 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/btn_radio_off_holo_light.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/btn_radio_on_holo_light.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/btn_radio_on_holo_light.png new file mode 100644 index 000000000..ed5acc92c Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/btn_radio_on_holo_light.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ic_menu_refresh_holo_light.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ic_menu_refresh_holo_light.png new file mode 100644 index 000000000..604a4fdf6 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ic_menu_refresh_holo_light.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ic_menu_star_holo_light.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ic_menu_star_holo_light.png new file mode 100644 index 000000000..77542c4db Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ic_menu_star_holo_light.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/ic_stat_android.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/ic_stat_android.png new file mode 100644 index 000000000..e91187d49 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/ic_stat_android.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/icon.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/icon.png new file mode 100644 index 000000000..79f50d84c Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/icon.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/spinner_default_holo_light.9.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/spinner_default_holo_light.9.png new file mode 100644 index 000000000..c820e40a1 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/spinner_default_holo_light.9.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-mdpi/spinner_disabled_holo_light.9.png b/actionbarsherlock/samples/styled/res/drawable-mdpi/spinner_disabled_holo_light.9.png new file mode 100644 index 000000000..2dba2704f Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-mdpi/spinner_disabled_holo_light.9.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable-xhdpi/icon.png b/actionbarsherlock/samples/styled/res/drawable-xhdpi/icon.png new file mode 100644 index 000000000..34ca9a489 Binary files /dev/null and b/actionbarsherlock/samples/styled/res/drawable-xhdpi/icon.png differ diff --git a/actionbarsherlock/samples/styled/res/drawable/actionbar_tab_bg.xml b/actionbarsherlock/samples/styled/res/drawable/actionbar_tab_bg.xml new file mode 100644 index 000000000..990990983 --- /dev/null +++ b/actionbarsherlock/samples/styled/res/drawable/actionbar_tab_bg.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/actionbarsherlock/samples/styled/res/drawable/ad_action_bar_gradient_bak.xml b/actionbarsherlock/samples/styled/res/drawable/ad_action_bar_gradient_bak.xml new file mode 100644 index 000000000..0233f4efc --- /dev/null +++ b/actionbarsherlock/samples/styled/res/drawable/ad_action_bar_gradient_bak.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/styled/res/drawable/ad_btn_check_holo_light.xml b/actionbarsherlock/samples/styled/res/drawable/ad_btn_check_holo_light.xml new file mode 100644 index 000000000..ee2c57e53 --- /dev/null +++ b/actionbarsherlock/samples/styled/res/drawable/ad_btn_check_holo_light.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/styled/res/drawable/ad_btn_radio_holo_light.xml b/actionbarsherlock/samples/styled/res/drawable/ad_btn_radio_holo_light.xml new file mode 100644 index 000000000..15fc3ba0f --- /dev/null +++ b/actionbarsherlock/samples/styled/res/drawable/ad_btn_radio_holo_light.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/styled/res/drawable/ad_selectable_background.xml b/actionbarsherlock/samples/styled/res/drawable/ad_selectable_background.xml new file mode 100644 index 000000000..bbb68004a --- /dev/null +++ b/actionbarsherlock/samples/styled/res/drawable/ad_selectable_background.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/styled/res/drawable/ad_spinner_background_holo_light.xml b/actionbarsherlock/samples/styled/res/drawable/ad_spinner_background_holo_light.xml new file mode 100644 index 000000000..d8d72bd27 --- /dev/null +++ b/actionbarsherlock/samples/styled/res/drawable/ad_spinner_background_holo_light.xml @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/actionbarsherlock/samples/styled/res/drawable/rounded_rect.xml b/actionbarsherlock/samples/styled/res/drawable/rounded_rect.xml new file mode 100644 index 000000000..bd0e341b4 --- /dev/null +++ b/actionbarsherlock/samples/styled/res/drawable/rounded_rect.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/styled/res/drawable/selected_background.xml b/actionbarsherlock/samples/styled/res/drawable/selected_background.xml new file mode 100644 index 000000000..546caf273 --- /dev/null +++ b/actionbarsherlock/samples/styled/res/drawable/selected_background.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/actionbarsherlock/samples/styled/res/layout/indeterminate_progress_action.xml b/actionbarsherlock/samples/styled/res/layout/indeterminate_progress_action.xml new file mode 100644 index 000000000..25b5ccbb7 --- /dev/null +++ b/actionbarsherlock/samples/styled/res/layout/indeterminate_progress_action.xml @@ -0,0 +1,23 @@ + + + + diff --git a/actionbarsherlock/samples/styled/res/layout/main.xml b/actionbarsherlock/samples/styled/res/layout/main.xml new file mode 100644 index 000000000..dacc55780 --- /dev/null +++ b/actionbarsherlock/samples/styled/res/layout/main.xml @@ -0,0 +1,21 @@ + + + + diff --git a/actionbarsherlock/samples/styled/res/menu-v11/main_menu.xml b/actionbarsherlock/samples/styled/res/menu-v11/main_menu.xml new file mode 100644 index 000000000..c705525bd --- /dev/null +++ b/actionbarsherlock/samples/styled/res/menu-v11/main_menu.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/styled/res/menu/main_menu.xml b/actionbarsherlock/samples/styled/res/menu/main_menu.xml new file mode 100644 index 000000000..edcf84f56 --- /dev/null +++ b/actionbarsherlock/samples/styled/res/menu/main_menu.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/samples/styled/res/values-v11/styles.xml b/actionbarsherlock/samples/styled/res/values-v11/styles.xml new file mode 100644 index 000000000..b790ce9e1 --- /dev/null +++ b/actionbarsherlock/samples/styled/res/values-v11/styles.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/styled/res/values/arrays.xml b/actionbarsherlock/samples/styled/res/values/arrays.xml new file mode 100644 index 000000000..9fc21674f --- /dev/null +++ b/actionbarsherlock/samples/styled/res/values/arrays.xml @@ -0,0 +1,27 @@ + + + + + + Home + SDK + Dev Guide + Reference + Resources + Videos + Blog + + \ No newline at end of file diff --git a/actionbarsherlock/samples/styled/res/values/colors.xml b/actionbarsherlock/samples/styled/res/values/colors.xml new file mode 100644 index 000000000..ec6af37fb --- /dev/null +++ b/actionbarsherlock/samples/styled/res/values/colors.xml @@ -0,0 +1,22 @@ + + + + + #FFA4C639 + #FF58BAED + #FF777777 + #FFEFEFEF + diff --git a/actionbarsherlock/samples/styled/res/values/dimens.xml b/actionbarsherlock/samples/styled/res/values/dimens.xml new file mode 100644 index 000000000..0b21229e0 --- /dev/null +++ b/actionbarsherlock/samples/styled/res/values/dimens.xml @@ -0,0 +1,20 @@ + + + + + 20dp + 8dp + \ No newline at end of file diff --git a/actionbarsherlock/samples/styled/res/values/strings.xml b/actionbarsherlock/samples/styled/res/values/strings.xml new file mode 100644 index 000000000..3bbe36138 --- /dev/null +++ b/actionbarsherlock/samples/styled/res/values/strings.xml @@ -0,0 +1,33 @@ + + + + + Refresh + Search + Both + Text + ABS: Styled Sample + Logo + Up + Tabs + List + Label + Navigation Mode > + Action Bar Background > + None + Gradient + Image + diff --git a/actionbarsherlock/samples/styled/res/values/styles.xml b/actionbarsherlock/samples/styled/res/values/styles.xml new file mode 100644 index 000000000..a2e0d6c8c --- /dev/null +++ b/actionbarsherlock/samples/styled/res/values/styles.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/samples/styled/src/com/actionbarsherlock/sample/styledactionbar/MainActivity.java b/actionbarsherlock/samples/styled/src/com/actionbarsherlock/sample/styledactionbar/MainActivity.java new file mode 100644 index 000000000..3321eac57 --- /dev/null +++ b/actionbarsherlock/samples/styled/src/com/actionbarsherlock/sample/styledactionbar/MainActivity.java @@ -0,0 +1,240 @@ +/* + * 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.sample.styledactionbar; + +import com.actionbarsherlock.sample.styledactionbar.R; +import android.animation.ObjectAnimator; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.ActionBar; +import android.support.v4.app.ActionBar.OnNavigationListener; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.support.v4.view.MenuItem.OnMenuItemClickListener; +import android.view.View; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.RotateAnimation; +import android.widget.ArrayAdapter; + + +public class MainActivity extends FragmentActivity implements ActionBar.TabListener { + + static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; + + private final Handler handler = new Handler(); + private RoundedColourFragment leftFrag; + private RoundedColourFragment rightFrag; + private boolean useLogo = false; + private boolean showHomeUp = false; + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + final ActionBar ab = getSupportActionBar(); + + // set defaults for logo & home up + ab.setDisplayHomeAsUpEnabled(showHomeUp); + ab.setDisplayUseLogoEnabled(useLogo); + + // set up tabs nav + for (int i = 1; i < 4; i++) { + ab.addTab(ab.newTab().setText("Tab " + i).setTabListener(this)); + } + + // set up list nav + ab.setListNavigationCallbacks(ArrayAdapter + .createFromResource(this, R.array.sections, + android.R.layout.simple_spinner_dropdown_item), + new OnNavigationListener() { + public boolean onNavigationItemSelected(int itemPosition, + long itemId) { + // FIXME add proper implementation + rotateLeftFrag(); + return false; + } + }); + + // default to tab navigation + showTabsNav(); + + // create a couple of simple fragments as placeholders + final int MARGIN = 16; + leftFrag = new RoundedColourFragment(getResources().getColor( + R.color.android_green), 1f, MARGIN, MARGIN / 2, MARGIN, MARGIN); + rightFrag = new RoundedColourFragment(getResources().getColor( + R.color.honeycombish_blue), 2f, MARGIN / 2, MARGIN, MARGIN, + MARGIN); + + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.add(R.id.root, leftFrag); + ft.add(R.id.root, rightFrag); + ft.commit(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main_menu, menu); + + // set up a listener for the refresh item + final MenuItem refresh = menu.findItem(R.id.menu_refresh); + refresh.setOnMenuItemClickListener(new OnMenuItemClickListener() { + // on selecting show progress spinner for 1s + public boolean onMenuItemClick(MenuItem item) { + // item.setActionView(R.layout.progress_action); + handler.postDelayed(new Runnable() { + public void run() { + refresh.setActionView(null); + } + }, 1000); + return false; + } + }); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // TODO handle clicking the app icon/logo + return false; + case R.id.menu_refresh: + // switch to a progress animation + item.setActionView(R.layout.indeterminate_progress_action); + return true; + case R.id.menu_both: + // rotation animation of green fragment + rotateLeftFrag(); + case R.id.menu_text: + // alpha animation of blue fragment + if (IS_HONEYCOMB) { + ObjectAnimatorAlpha.invoke(rightFrag.getView()); + } else { + AlphaAnimation alpha = new AlphaAnimation(1f, 0f); + alpha.setRepeatMode(Animation.REVERSE); + alpha.setRepeatCount(1); + alpha.setDuration(800); + rightFrag.getView().startAnimation(alpha); + } + return true; + case R.id.menu_logo: + useLogo = !useLogo; + item.setChecked(useLogo); + getSupportActionBar().setDisplayUseLogoEnabled(useLogo); + return true; + case R.id.menu_up: + showHomeUp = !showHomeUp; + item.setChecked(showHomeUp); + getSupportActionBar().setDisplayHomeAsUpEnabled(showHomeUp); + return true; + case R.id.menu_nav_tabs: + item.setChecked(true); + showTabsNav(); + return true; + case R.id.menu_nav_label: + item.setChecked(true); + showStandardNav(); + return true; + case R.id.menu_nav_drop_down: + item.setChecked(true); + showDropDownNav(); + return true; + case R.id.menu_bak_none: + item.setChecked(true); + getSupportActionBar().setBackgroundDrawable(null); + return true; + case R.id.menu_bak_gradient: + item.setChecked(true); + getSupportActionBar().setBackgroundDrawable(getResources().getDrawable(R.drawable.ad_action_bar_gradient_bak)); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private static final class ObjectAnimatorAlpha { + static void invoke(View view) { + ObjectAnimator alpha = ObjectAnimator.ofFloat(view, + "alpha", 1f, 0f); + alpha.setRepeatMode(ObjectAnimator.REVERSE); + alpha.setRepeatCount(1); + alpha.setDuration(800); + alpha.start(); + } + } + + private void rotateLeftFrag() { + if (leftFrag != null) { + if (IS_HONEYCOMB) { + ObjectAnimatorRotate.invoke(leftFrag.getView()); + } else { + RotateAnimation rotate = new RotateAnimation(0, 180, leftFrag.getView().getWidth() / 2.0f, leftFrag.getView().getHeight() / 2.0f); + rotate.setDuration(500); + leftFrag.getView().startAnimation(rotate); + } + } + } + + private static final class ObjectAnimatorRotate { + static void invoke(View view) { + ObjectAnimator.ofFloat(view, "rotationY", 0, 180) + .setDuration(500).start(); + } + } + + private void showStandardNav() { + ActionBar ab = getSupportActionBar(); + if (ab.getNavigationMode() != ActionBar.NAVIGATION_MODE_STANDARD) { + ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); + } + } + + private void showDropDownNav() { + ActionBar ab = getSupportActionBar(); + if (ab.getNavigationMode() != ActionBar.NAVIGATION_MODE_LIST) { + ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); + } + } + + private void showTabsNav() { + ActionBar ab = getSupportActionBar(); + if (ab.getNavigationMode() != ActionBar.NAVIGATION_MODE_TABS) { + ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + } + } + + public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) { + // FIXME add a proper implementation, for now just rotate the left + // fragment + rotateLeftFrag(); + } + + public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { + // FIXME implement this + } + + public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { + // FIXME implement this + } + +} \ No newline at end of file diff --git a/actionbarsherlock/samples/styled/src/com/actionbarsherlock/sample/styledactionbar/RoundedColourFragment.java b/actionbarsherlock/samples/styled/src/com/actionbarsherlock/sample/styledactionbar/RoundedColourFragment.java new file mode 100644 index 000000000..5a8105a53 --- /dev/null +++ b/actionbarsherlock/samples/styled/src/com/actionbarsherlock/sample/styledactionbar/RoundedColourFragment.java @@ -0,0 +1,68 @@ +/* + * 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.sample.styledactionbar; + +import com.actionbarsherlock.sample.styledactionbar.R; +import android.graphics.drawable.GradientDrawable; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.widget.LinearLayout; + +public class RoundedColourFragment extends Fragment { + private View mView; + private int mColor; + private float mWeight; + private int mMarginLeft; + private int mMarginRight; + private int mMarginTop; + private int mMarginBottom; + + // need a public empty constructor for framework to instantiate + public RoundedColourFragment() {} + + public RoundedColourFragment(int color, float weight, int margin_left, int margin_right, int margin_top, int margin_bottom) { + this.mColor = color; + this.mWeight = weight; + this.mMarginLeft = margin_left; + this.mMarginRight = margin_right; + this.mMarginTop = margin_top; + this.mMarginBottom = margin_bottom; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.mView = new View(this.getActivity()); + + GradientDrawable background = (GradientDrawable)this.getResources().getDrawable(R.drawable.rounded_rect); + background.setColor(this.mColor); + + this.mView.setBackgroundDrawable(background); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0, LayoutParams.FILL_PARENT, mWeight); + lp.setMargins(this.mMarginLeft, this.mMarginTop, this.mMarginRight, this.mMarginBottom); + this.mView.setLayoutParams(lp); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return this.mView; + } +} diff --git a/actionbarsherlock/test/README.md b/actionbarsherlock/test/README.md new file mode 100644 index 000000000..332a6f1d2 --- /dev/null +++ b/actionbarsherlock/test/README.md @@ -0,0 +1,11 @@ +ActionBarSherlock Test +====================== + +This folder contains two applications which perform unit testing against the +main library. + + * `app/` - Individual activities which each set up and provide interaction + methods for a specific test case. + + * `runner/` - Suite of test cases that verify each activity and method + provided by `app/` are operating as expected. diff --git a/actionbarsherlock/test/app/AndroidManifest.xml b/actionbarsherlock/test/app/AndroidManifest.xml new file mode 100644 index 000000000..ec871bde4 --- /dev/null +++ b/actionbarsherlock/test/app/AndroidManifest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/actionbarsherlock/test/app/README.md b/actionbarsherlock/test/app/README.md new file mode 100644 index 000000000..b84d6e69e --- /dev/null +++ b/actionbarsherlock/test/app/README.md @@ -0,0 +1,9 @@ +ActionBarSherlock Test App +========================== + +Individual activities which each set up and provide interaction methods for a +specific test case. + +Each activity has a name which provides insight into the what it is testing. If +you are providing new activities to test please follow the same naming +conventions that are already in use. diff --git a/actionbarsherlock/test/app/pom.xml b/actionbarsherlock/test/app/pom.xml new file mode 100644 index 000000000..956db7776 --- /dev/null +++ b/actionbarsherlock/test/app/pom.xml @@ -0,0 +1,52 @@ + + + + 4.0.0 + + com.actionbarsherlock + test-app + Test: Application + apk + + + com.actionbarsherlock + parent-test + 3.4.2 + ../pom.xml + + + + + android + android + provided + + + + com.actionbarsherlock + library + ${project.version} + apklib + + + + + src + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + + diff --git a/actionbarsherlock/test/app/project.properties b/actionbarsherlock/test/app/project.properties new file mode 100644 index 000000000..34f549f8c --- /dev/null +++ b/actionbarsherlock/test/app/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. + +# Project target. +target=android-13 +android.library.reference.1=../../library diff --git a/actionbarsherlock/test/app/res/drawable-hdpi/icon.png b/actionbarsherlock/test/app/res/drawable-hdpi/icon.png new file mode 100644 index 000000000..723fbf062 Binary files /dev/null and b/actionbarsherlock/test/app/res/drawable-hdpi/icon.png differ diff --git a/actionbarsherlock/test/app/res/drawable-ldpi/icon.png b/actionbarsherlock/test/app/res/drawable-ldpi/icon.png new file mode 100644 index 000000000..181f679f5 Binary files /dev/null and b/actionbarsherlock/test/app/res/drawable-ldpi/icon.png differ diff --git a/actionbarsherlock/test/app/res/drawable-mdpi/ic_menu_star_holo_light.png b/actionbarsherlock/test/app/res/drawable-mdpi/ic_menu_star_holo_light.png new file mode 100644 index 000000000..77542c4db Binary files /dev/null and b/actionbarsherlock/test/app/res/drawable-mdpi/ic_menu_star_holo_light.png differ diff --git a/actionbarsherlock/test/app/res/drawable-mdpi/icon.png b/actionbarsherlock/test/app/res/drawable-mdpi/icon.png new file mode 100644 index 000000000..79f50d84c Binary files /dev/null and b/actionbarsherlock/test/app/res/drawable-mdpi/icon.png differ diff --git a/actionbarsherlock/test/app/res/drawable-xhdpi/icon.png b/actionbarsherlock/test/app/res/drawable-xhdpi/icon.png new file mode 100644 index 000000000..34ca9a489 Binary files /dev/null and b/actionbarsherlock/test/app/res/drawable-xhdpi/icon.png differ diff --git a/actionbarsherlock/test/app/res/layout/blank.xml b/actionbarsherlock/test/app/res/layout/blank.xml new file mode 100644 index 000000000..34dc540b5 --- /dev/null +++ b/actionbarsherlock/test/app/res/layout/blank.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/actionbarsherlock/test/app/res/layout/issue0038.xml b/actionbarsherlock/test/app/res/layout/issue0038.xml new file mode 100644 index 000000000..dcfcb2549 --- /dev/null +++ b/actionbarsherlock/test/app/res/layout/issue0038.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/test/app/res/menu/issue0031.xml b/actionbarsherlock/test/app/res/menu/issue0031.xml new file mode 100644 index 000000000..f6c4be5c6 --- /dev/null +++ b/actionbarsherlock/test/app/res/menu/issue0031.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/test/app/res/menu/issue0033.xml b/actionbarsherlock/test/app/res/menu/issue0033.xml new file mode 100644 index 000000000..a6aa1519d --- /dev/null +++ b/actionbarsherlock/test/app/res/menu/issue0033.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/actionbarsherlock/test/app/res/values/strings.xml b/actionbarsherlock/test/app/res/values/strings.xml new file mode 100644 index 000000000..7af366e3b --- /dev/null +++ b/actionbarsherlock/test/app/res/values/strings.xml @@ -0,0 +1,7 @@ + + + + Resource title test. + Test + Hello. I Am A Test Title. + \ No newline at end of file diff --git a/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/FeatureCustomView.java b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/FeatureCustomView.java new file mode 100644 index 000000000..629e3bd95 --- /dev/null +++ b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/FeatureCustomView.java @@ -0,0 +1,45 @@ +package com.actionbarsherlock.tests.app; + +import java.util.concurrent.CountDownLatch; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.view.ViewGroup.LayoutParams; +import android.widget.TextView; + +public final class FeatureCustomView extends FragmentActivity { + public TextView customView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.blank); + } + + public void setCustomView() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + runOnUiThread(new Runnable() { + @Override + public void run() { + getSupportActionBar().setDisplayShowCustomEnabled(false); + customView = new TextView(FeatureCustomView.this); + customView.setText("Custom View!"); + customView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); + getSupportActionBar().setCustomView(customView); + latch.countDown(); + } + }); + latch.await(); + } + + public void enableCustomView() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + runOnUiThread(new Runnable() { + @Override + public void run() { + getSupportActionBar().setDisplayShowCustomEnabled(true); + latch.countDown(); + } + }); + latch.await(); + } +} diff --git a/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/FeatureEnableActionItemText.java b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/FeatureEnableActionItemText.java new file mode 100644 index 000000000..9e2a5a043 --- /dev/null +++ b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/FeatureEnableActionItemText.java @@ -0,0 +1,24 @@ +package com.actionbarsherlock.tests.app; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.support.v4.view.Window; + +public final class FeatureEnableActionItemText extends FragmentActivity { + public static final String MENU_ITEM_TEXT = "Item"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_ACTION_BAR_ITEM_TEXT); + setContentView(R.layout.blank); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(MENU_ITEM_TEXT).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + return true; + } +} diff --git a/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0002.java b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0002.java new file mode 100644 index 000000000..5ac2e1686 --- /dev/null +++ b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0002.java @@ -0,0 +1,54 @@ +package com.actionbarsherlock.tests.app; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; +import android.support.v4.view.Window; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +public final class Issue0002 extends FragmentActivity { + public static final String MENU_ITEM_TEXT = "Click"; + + public boolean triggered = false; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_ACTION_BAR_ITEM_TEXT); + + getSupportFragmentManager().beginTransaction() + .add(android.R.id.content, new TestFragment()) + .commit(); + } + + public final class TestFragment extends Fragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add(0, 0, 0, MENU_ITEM_TEXT) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return new FrameLayout(getActivity()); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + triggered = true; + return true; + } + } +} \ No newline at end of file diff --git a/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0030.java b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0030.java new file mode 100644 index 000000000..588470cb6 --- /dev/null +++ b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0030.java @@ -0,0 +1,58 @@ +package com.actionbarsherlock.tests.app; + +import java.util.concurrent.CountDownLatch; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; + +public final class Issue0030 extends FragmentActivity { + private boolean performFindItem = false; + private boolean performRemoveItem = false; + private MenuItem findItemResult; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (performFindItem) { + findItemResult = menu.findItem(1); + } else if (performRemoveItem) { + menu.removeItem(1); + } + return false; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.blank); + } + + public MenuItem performFindItem() throws InterruptedException { + performFindItem = true; + final CountDownLatch latch = new CountDownLatch(1); + runOnUiThread(new Runnable() { + @Override + public void run() { + invalidateOptionsMenu(); + latch.countDown(); + } + }); + latch.await(); + performFindItem = false; + return findItemResult; + } + + public void performRemoveItem() throws InterruptedException { + performRemoveItem = true; + final CountDownLatch latch = new CountDownLatch(1); + runOnUiThread(new Runnable() { + @Override + public void run() { + invalidateOptionsMenu(); + latch.countDown(); + } + }); + latch.await(); + performRemoveItem = false; + } +} \ No newline at end of file diff --git a/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0031.java b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0031.java new file mode 100644 index 000000000..2b8247084 --- /dev/null +++ b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0031.java @@ -0,0 +1,39 @@ +package com.actionbarsherlock.tests.app; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; + +public final class Issue0031 extends FragmentActivity { + private MenuItem mResourceMenuItem; + private MenuItem mStringMenuItem; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.blank); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.issue0031, menu); + mResourceMenuItem = menu.findItem(R.id.issue0031_resource_item); + mStringMenuItem = menu.findItem(R.id.issue0031_string_item); + return false; + } + + public String getResourceTitle() { + return getString(R.string.issue0031_test); + } + public String getStringTitle() { + //Make sure this matches EXATCLY with the menu.xml value. + return "String title test."; + } + public MenuItem getResourceMenuItem() { + return mResourceMenuItem; + } + public MenuItem getStringMenuItem() { + return mStringMenuItem; + } +} \ No newline at end of file diff --git a/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0033.java b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0033.java new file mode 100644 index 000000000..531d66b0b --- /dev/null +++ b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0033.java @@ -0,0 +1,78 @@ +package com.actionbarsherlock.tests.app; + +import java.util.concurrent.CountDownLatch; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; +import android.support.v4.view.MenuItem; + +public final class Issue0033 extends FragmentActivity { + public static final int ID_VISIBLE = 1; + public static final int ID_HIDDEN = 2; + public static final String TEXT_VISIBLE = "Test1"; + public static final String TEXT_HIDDEN = "Test2"; + + public MenuItem menuItemNativeVisible; + public MenuItem menuItemNativeXmlHidden; + public MenuItem menuItemNativeCodeHidden; + public MenuItem menuItemActionVisible; + public MenuItem menuItemActionXmlHidden; + public MenuItem menuItemActionCodeHidden; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.blank); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menuItemNativeVisible = menu.add(0, ID_VISIBLE, 0, TEXT_VISIBLE); + menuItemNativeVisible.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + + menuItemActionVisible = menu.add(0, 0, 0, TEXT_VISIBLE); + menuItemActionVisible.setIcon(R.drawable.ic_menu_star_holo_light); + menuItemActionVisible.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + getMenuInflater().inflate(R.menu.issue0033, menu); + menuItemActionXmlHidden = menu.findItem(R.id.issue0033_action); + menuItemNativeXmlHidden = menu.findItem(R.id.issue0033_native); + + menuItemNativeCodeHidden = menu.add(0, ID_HIDDEN, 0, TEXT_HIDDEN); + menuItemNativeCodeHidden.setVisible(false); + menuItemActionCodeHidden = menu.add(0, 0, 0, TEXT_HIDDEN); + menuItemActionCodeHidden.setIcon(R.drawable.ic_menu_star_holo_light); + menuItemActionCodeHidden.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + menuItemActionCodeHidden.setVisible(false); + + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case ID_VISIBLE: + case ID_HIDDEN: + case R.id.issue0033_native: + return item.isVisible(); + } + return super.onOptionsItemSelected(item); + } + + public String getXmlHiddenText() { + return getString(R.string.issue0033_test); + } + + public void hideVisibleMenuItems() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + runOnUiThread(new Runnable() { + @Override + public void run() { + menuItemActionVisible.setVisible(false); + menuItemNativeVisible.setVisible(false); + latch.countDown(); + } + }); + latch.await(); + } +} diff --git a/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0035.java b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0035.java new file mode 100644 index 000000000..8eb7e87ce --- /dev/null +++ b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0035.java @@ -0,0 +1,30 @@ +package com.actionbarsherlock.tests.app; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; + +public final class Issue0035 extends FragmentActivity { + boolean mIsActivityCreating = false; + boolean mWasMenuCreatedOnActivityCreation = false; + + @Override + public void onCreate(Bundle savedInstanceState) { + mIsActivityCreating = true; + super.onCreate(savedInstanceState); + mIsActivityCreating = false; + setContentView(R.layout.blank); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (mIsActivityCreating) { + mWasMenuCreatedOnActivityCreation = true; + } + return false; + } + + public boolean getWasMenuCreatedOnActivityCreation() { + return mWasMenuCreatedOnActivityCreation; + } +} \ No newline at end of file diff --git a/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0036.java b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0036.java new file mode 100644 index 000000000..c50fcd59a --- /dev/null +++ b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0036.java @@ -0,0 +1,35 @@ +package com.actionbarsherlock.tests.app; + +import java.util.concurrent.CountDownLatch; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; + +public final class Issue0036 extends FragmentActivity { + Object subMenuItemParent = null; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.blank); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + subMenuItemParent = menu.addSubMenu("test").getItem(); + return false; + } + + public Object getSubMenuItemParent() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + runOnUiThread(new Runnable() { + @Override + public void run() { + invalidateOptionsMenu(); + latch.countDown(); + } + }); + latch.await(); + return subMenuItemParent; + } +} \ No newline at end of file diff --git a/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0038.java b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0038.java new file mode 100644 index 000000000..024b03d3d --- /dev/null +++ b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0038.java @@ -0,0 +1,31 @@ +package com.actionbarsherlock.tests.app; + +import android.app.TabActivity; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.widget.TabHost; + +@SuppressWarnings("deprecation") +public final class Issue0038 extends TabActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.issue0038); + + TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost); + tabHost.addTab( + tabHost.newTabSpec("tab1") + .setIndicator("First Tab Name") + .setContent(new Intent(this, InnerActivity.class)) + ); + } + + public static class InnerActivity extends FragmentActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.blank); + } + } +} \ No newline at end of file diff --git a/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0042.java b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0042.java new file mode 100644 index 000000000..72b38d108 --- /dev/null +++ b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0042.java @@ -0,0 +1,54 @@ +package com.actionbarsherlock.tests.app; + +import java.util.concurrent.CountDownLatch; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +public final class Issue0042 extends FragmentActivity { + public static final String TITLE = "Hey, This Is A Test!"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.blank); + } + + public void setTitleString() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + runOnUiThread(new Runnable() { + @Override + public void run() { + setTitle(TITLE); + latch.countDown(); + } + }); + latch.await(); + } + + public String setTitleResource() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + runOnUiThread(new Runnable() { + @Override + public void run() { + setTitle(R.string.issue0042_title); + latch.countDown(); + } + }); + latch.await(); + return getString(R.string.issue0042_title); + } + + private CharSequence supportActionBarTitle = null; + public CharSequence getSupportActionBarTitle() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + runOnUiThread(new Runnable() { + @Override + public void run() { + supportActionBarTitle = getSupportActionBar().getTitle(); + latch.countDown(); + } + }); + latch.await(); + return supportActionBarTitle; + } +} \ No newline at end of file diff --git a/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0045.java b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0045.java new file mode 100644 index 000000000..935c5bc22 --- /dev/null +++ b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0045.java @@ -0,0 +1,35 @@ +package com.actionbarsherlock.tests.app; + +import java.util.concurrent.CountDownLatch; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.Menu; + +public final class Issue0045 extends FragmentActivity { + Object mMenuItemSubMenu = null; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.blank); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + mMenuItemSubMenu = menu.addSubMenu("test").getItem().getSubMenu(); + return false; + } + + public Object getMenuItemSubMenu() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + runOnUiThread(new Runnable() { + @Override + public void run() { + invalidateOptionsMenu(); + latch.countDown(); + } + }); + latch.await(); + return mMenuItemSubMenu; + } +} \ No newline at end of file diff --git a/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0048.java b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0048.java new file mode 100644 index 000000000..e30b0e097 --- /dev/null +++ b/actionbarsherlock/test/app/src/com/actionbarsherlock/tests/app/Issue0048.java @@ -0,0 +1,75 @@ +package com.actionbarsherlock.tests.app; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.Menu; +import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public final class Issue0048 extends FragmentActivity { + private Menu mMenu; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + ViewPager pager = new ViewPager(this); + pager.setId(1); + setContentView(pager); + + pager.setAdapter(new TestAdapter(getSupportFragmentManager())); + invalidateOptionsMenu(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + mMenu = menu; + return super.onCreateOptionsMenu(menu); + } + + public Menu getMenu() { + return mMenu; + } + + static final class TestAdapter extends FragmentPagerAdapter { + public TestAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public int getCount() { + return 2; + } + + @Override + public Fragment getItem(int position) { + return new TestFragment(); + } + } + + public static class TestFragment extends Fragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return new TextView(getActivity()); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add("Test"); + } + + } +} \ No newline at end of file diff --git a/actionbarsherlock/test/pom.xml b/actionbarsherlock/test/pom.xml new file mode 100644 index 000000000..179b4dcfb --- /dev/null +++ b/actionbarsherlock/test/pom.xml @@ -0,0 +1,22 @@ + + + + 4.0.0 + + com.actionbarsherlock + parent-test + Test (Parent) + pom + + + com.actionbarsherlock + parent + 3.4.2 + ../pom.xml + + + + app + runner + + diff --git a/actionbarsherlock/test/runner/AndroidManifest.xml b/actionbarsherlock/test/runner/AndroidManifest.xml new file mode 100644 index 000000000..01aea3af3 --- /dev/null +++ b/actionbarsherlock/test/runner/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + diff --git a/actionbarsherlock/test/runner/README.md b/actionbarsherlock/test/runner/README.md new file mode 100644 index 000000000..5b856cd2e --- /dev/null +++ b/actionbarsherlock/test/runner/README.md @@ -0,0 +1,11 @@ +ActionBarSherlock Test Runner +============================= + +Suite of test cases that verify each activity and method provided by the test +app are operating as expected. + +Each class has a name which provides insight into the what it is testing and is +then prefixed by `Test`. If you are providing new test cases please follow the +same naming conventions that are already in use. If the test cases corresponds +to an activity in the test app please ensure that you use the same name for +both classes. diff --git a/actionbarsherlock/test/runner/pom.xml b/actionbarsherlock/test/runner/pom.xml new file mode 100644 index 000000000..f3460695c --- /dev/null +++ b/actionbarsherlock/test/runner/pom.xml @@ -0,0 +1,75 @@ + + + + 4.0.0 + + com.actionbarsherlock + test-runner + Test: Runner + apk + + + com.actionbarsherlock + parent-test + 3.4.2 + ../pom.xml + + + + + android + android + provided + + + + com.actionbarsherlock + test-app + ${project.version} + apk + + + + com.actionbarsherlock + test-app + ${project.version} + jar + + + + + false + + + + + test + + true + + + + + + src + + + + com.jayway.maven.plugins.android.generation2 + maven-android-plugin + + ${enable-integration-test} + + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + + diff --git a/actionbarsherlock/test/runner/project.properties b/actionbarsherlock/test/runner/project.properties new file mode 100644 index 000000000..b904742f6 --- /dev/null +++ b/actionbarsherlock/test/runner/project.properties @@ -0,0 +1,11 @@ +# 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. + +# Project target. +target=android-13 diff --git a/actionbarsherlock/test/runner/res/drawable-hdpi/icon.png b/actionbarsherlock/test/runner/res/drawable-hdpi/icon.png new file mode 100644 index 000000000..723fbf062 Binary files /dev/null and b/actionbarsherlock/test/runner/res/drawable-hdpi/icon.png differ diff --git a/actionbarsherlock/test/runner/res/drawable-ldpi/icon.png b/actionbarsherlock/test/runner/res/drawable-ldpi/icon.png new file mode 100644 index 000000000..181f679f5 Binary files /dev/null and b/actionbarsherlock/test/runner/res/drawable-ldpi/icon.png differ diff --git a/actionbarsherlock/test/runner/res/drawable-mdpi/icon.png b/actionbarsherlock/test/runner/res/drawable-mdpi/icon.png new file mode 100644 index 000000000..79f50d84c Binary files /dev/null and b/actionbarsherlock/test/runner/res/drawable-mdpi/icon.png differ diff --git a/actionbarsherlock/test/runner/res/drawable-xhdpi/icon.png b/actionbarsherlock/test/runner/res/drawable-xhdpi/icon.png new file mode 100644 index 000000000..34ca9a489 Binary files /dev/null and b/actionbarsherlock/test/runner/res/drawable-xhdpi/icon.png differ diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/BaseTestCase.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/BaseTestCase.java new file mode 100644 index 000000000..8dc67f7e6 --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/BaseTestCase.java @@ -0,0 +1,109 @@ +package com.actionbarsherlock.tests.runner; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import android.app.Activity; +import android.os.Build; +import android.test.ActivityInstrumentationTestCase2; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +public abstract class BaseTestCase extends ActivityInstrumentationTestCase2 { + protected static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; + + public BaseTestCase(Class clazz) { + super(clazz); + } + + protected View findViewByClassName(View root, String className) { + LinkedList lookupQueue = new LinkedList(); + lookupQueue.add(root); + + while (!lookupQueue.isEmpty()) { + View target = lookupQueue.removeFirst(); + if (className.equals(target.getClass().getName())) { + return target; + } + if (target instanceof ViewGroup) { + ViewGroup targetGroup = (ViewGroup)target; + for (int i = 0, count = targetGroup.getChildCount(); i < count; i++) { + lookupQueue.addLast(targetGroup.getChildAt(i)); + } + } + } + + return null; + } + + protected List findViewsByClassName(View root, String className) { + LinkedList results = new LinkedList(); + LinkedList lookupQueue = new LinkedList(); + lookupQueue.add(root); + + while (!lookupQueue.isEmpty()) { + View target = lookupQueue.removeFirst(); + if (className.equals(target.getClass().getName())) { + results.add(target); + } else if (target instanceof ViewGroup) { + ViewGroup targetGroup = (ViewGroup)target; + for (int i = 0, count = targetGroup.getChildCount(); i < count; i++) { + lookupQueue.addLast(targetGroup.getChildAt(i)); + } + } + } + + return results; + } + + protected View findActionItem(final String text) { + List items = null; + if (IS_HONEYCOMB) { + items = findViewsByClassName(getActivity().getWindow().getDecorView(), "com.android.internal.view.menu.ActionMenuItemView"); + for (View item : items) { + Button textView = (Button)findViewByClassName(item, "android.widget.Button"); + if (textView != null && text.equals(textView.getText())) { + return textView; + } + } + } else { + items = findViewsByClassName(getActivity().getWindow().getDecorView(), "com.actionbarsherlock.internal.view.menu.ActionMenuItemView"); + for (View item : items) { + TextView textView = (TextView)findViewByClassName(item, "android.widget.TextView"); + if (textView != null && text.equals(textView.getText())) { + return item; + } + } + } + return null; + } + + protected void clickActionItem(final String text) { + final View result = findActionItem(text); + if (result != null) { + final CountDownLatch latch = new CountDownLatch(1); + + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + result.performClick(); + latch.countDown(); + } + }); + + try { + latch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + @Override + protected void tearDown() throws Exception { + getActivity().finish(); + super.tearDown(); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestFeatureCustomView.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestFeatureCustomView.java new file mode 100644 index 000000000..96c87d265 --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestFeatureCustomView.java @@ -0,0 +1,25 @@ +package com.actionbarsherlock.tests.runner; + +import android.test.suitebuilder.annotation.Smoke; +import com.actionbarsherlock.tests.app.FeatureCustomView; + +public class TestFeatureCustomView extends BaseTestCase { + public TestFeatureCustomView() { + super(FeatureCustomView.class); + } + + @Smoke + public void testCustomViewDisplayedWhenEnabled() throws InterruptedException { + getActivity().setCustomView(); + getActivity().enableCustomView(); + + assertTrue("Custom view not displayed when enabled.", getActivity().customView.isShown()); + } + + @Smoke + public void testCustomViewNotDisplayedWhenAssigned() throws InterruptedException { + getActivity().setCustomView(); + + assertTrue("Custom view displayed when assigned.", !getActivity().customView.isShown()); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestFeatureEnableActionItemText.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestFeatureEnableActionItemText.java new file mode 100644 index 000000000..7dbdd19a3 --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestFeatureEnableActionItemText.java @@ -0,0 +1,19 @@ +package com.actionbarsherlock.tests.runner; + +import com.actionbarsherlock.tests.app.FeatureEnableActionItemText; +import android.test.suitebuilder.annotation.Smoke; + +public class TestFeatureEnableActionItemText extends BaseTestCase { + public TestFeatureEnableActionItemText() { + super(FeatureEnableActionItemText.class); + } + + @Smoke + public void testFragmentReceivesOnMenuItemSelectedCallback() { + if (IS_HONEYCOMB) { + return; + } + + assertNotNull("Text-only action-item could not be found.", findActionItem(FeatureEnableActionItemText.MENU_ITEM_TEXT)); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0002.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0002.java new file mode 100644 index 000000000..152f73275 --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0002.java @@ -0,0 +1,17 @@ +package com.actionbarsherlock.tests.runner; + +import android.test.suitebuilder.annotation.Smoke; +import com.actionbarsherlock.tests.app.Issue0002; + +public class TestIssue0002 extends BaseTestCase { + public TestIssue0002() { + super(Issue0002.class); + } + + @Smoke + public void testFragmentReceivesOnMenuItemSelectedCallback() throws InterruptedException { + assertFalse(getActivity().triggered); + clickActionItem(Issue0002.MENU_ITEM_TEXT); + assertTrue(getActivity().triggered); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0030.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0030.java new file mode 100644 index 000000000..0ebac462d --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0030.java @@ -0,0 +1,21 @@ +package com.actionbarsherlock.tests.runner; + +import android.test.suitebuilder.annotation.Smoke; +import com.actionbarsherlock.tests.app.Issue0030; + +public class TestIssue0030 extends BaseTestCase { + public TestIssue0030() { + super(Issue0030.class); + } + + @Smoke + public void testMenuFindItemDoesNotError() throws InterruptedException { + assertNull(getActivity().performFindItem()); + } + + @Smoke + public void testMenuRemoveItemDoesNotError() throws InterruptedException { + getActivity().performRemoveItem(); + assertTrue(true); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0031.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0031.java new file mode 100644 index 000000000..d9000448a --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0031.java @@ -0,0 +1,31 @@ +package com.actionbarsherlock.tests.runner; + +import android.support.v4.view.MenuItem; +import android.test.suitebuilder.annotation.Smoke; +import com.actionbarsherlock.tests.app.Issue0031; + +public class TestIssue0031 extends BaseTestCase { + public TestIssue0031() { + super(Issue0031.class); + } + + @Smoke + public void testMenuItemResourceTitle() { + String expected = getActivity().getResourceTitle(); + MenuItem resourceMenuItem = getActivity().getResourceMenuItem(); + assertNotNull(resourceMenuItem); + CharSequence actual = resourceMenuItem.getTitle(); + assertNotNull(actual); + assertEquals(expected, actual); + } + + @Smoke + public void testMenuItemStringTitle() { + String expected = getActivity().getStringTitle(); + MenuItem stringMenuItem = getActivity().getStringMenuItem(); + assertNotNull(stringMenuItem); + CharSequence actual = stringMenuItem.getTitle(); + assertNotNull(actual); + assertEquals(expected, actual); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0033.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0033.java new file mode 100644 index 000000000..31426e6aa --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0033.java @@ -0,0 +1,69 @@ +package com.actionbarsherlock.tests.runner; + +import android.support.v4.view.MenuItem; +import android.view.View; +import com.actionbarsherlock.tests.app.Issue0033; +import com.actionbarsherlock.tests.app.R; + +public class TestIssue0033 extends BaseTestCase { + public TestIssue0033() { + super(Issue0033.class); + } + + public void testVisibleMenuItemHiding() throws InterruptedException { + //Get native item + MenuItem nativeItem = getActivity().menuItemNativeVisible; + assertNotNull(nativeItem); + assertTrue(nativeItem.isVisible()); + assertTrue(getInstrumentation().invokeMenuActionSync(getActivity(), Issue0033.ID_VISIBLE, 0)); + + //Get action item + MenuItem actionItem = getActivity().menuItemActionVisible; + assertNotNull(actionItem); + assertTrue(actionItem.isVisible()); + + //Get action item view + View actionItemView = findActionItem(Issue0033.TEXT_VISIBLE); + assertNotNull(actionItemView); + assertEquals(View.VISIBLE, actionItemView.getVisibility()); + + //Hide and test hidden + getActivity().hideVisibleMenuItems(); + assertFalse(nativeItem.isVisible()); + assertFalse(getInstrumentation().invokeMenuActionSync(getActivity(), Issue0033.ID_VISIBLE, 0)); + assertFalse(actionItem.isVisible()); + assertEquals(View.GONE, actionItemView.getVisibility()); + } + + public void testHiddenMenuItems() throws InterruptedException { + //Get the menu items + MenuItem actionXmlHidden = getActivity().menuItemActionXmlHidden; + MenuItem actionCodeHidden = getActivity().menuItemActionCodeHidden; + MenuItem nativeXmlHidden = getActivity().menuItemNativeXmlHidden; + MenuItem nativeCodeHidden = getActivity().menuItemNativeCodeHidden; + + //Make sure they all were created properly + assertNotNull(actionXmlHidden); + assertNotNull(actionCodeHidden); + assertNotNull(nativeXmlHidden); + assertNotNull(nativeCodeHidden); + + //Make sure the items think they're hidden + assertFalse(actionXmlHidden.isVisible()); + assertFalse(actionCodeHidden.isVisible()); + assertFalse(nativeXmlHidden.isVisible()); + assertFalse(nativeCodeHidden.isVisible()); + + //Test native items are not present + assertFalse(getInstrumentation().invokeMenuActionSync(getActivity(), Issue0033.ID_HIDDEN, 0)); + assertFalse(getInstrumentation().invokeMenuActionSync(getActivity(), R.id.issue0033_native, 0)); + + //Test action items are not present + View codeView = findActionItem(Issue0033.TEXT_HIDDEN); + assertNotNull(codeView); + assertEquals(View.GONE, codeView.getVisibility()); + View xmlView = findActionItem(getActivity().getXmlHiddenText()); + assertNotNull(xmlView); + assertEquals(View.GONE, xmlView.getVisibility()); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0035.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0035.java new file mode 100644 index 000000000..cc7d15755 --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0035.java @@ -0,0 +1,15 @@ +package com.actionbarsherlock.tests.runner; + +import android.test.suitebuilder.annotation.Smoke; +import com.actionbarsherlock.tests.app.Issue0035; + +public class TestIssue0035 extends BaseTestCase { + public TestIssue0035() { + super(Issue0035.class); + } + + @Smoke + public void testMenuCreatingOnActivityCreation() throws InterruptedException { + assertFalse(getActivity().getWasMenuCreatedOnActivityCreation()); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0036.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0036.java new file mode 100644 index 000000000..8ead681ef --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0036.java @@ -0,0 +1,17 @@ +package com.actionbarsherlock.tests.runner; + +import android.test.suitebuilder.annotation.Smoke; +import com.actionbarsherlock.tests.app.Issue0036; + +public class TestIssue0036 extends BaseTestCase { + public TestIssue0036() { + super(Issue0036.class); + } + + @Smoke + public void testMenuFindItemDoesNotError() throws InterruptedException { + Object menuItem = getActivity().getSubMenuItemParent(); + assertNotNull(menuItem); + assertTrue(menuItem instanceof android.support.v4.view.MenuItem); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0038.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0038.java new file mode 100644 index 000000000..7a5517318 --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0038.java @@ -0,0 +1,21 @@ +package com.actionbarsherlock.tests.runner; + +import android.test.suitebuilder.annotation.Smoke; +import com.actionbarsherlock.tests.app.Issue0038; + +public class TestIssue0038 extends BaseTestCase { + public TestIssue0038() { + super(Issue0038.class); + } + + @Smoke + public void testChildActivityHasNoActionBar() throws InterruptedException { + String className; + if (IS_HONEYCOMB) { + className = "com.android.internal.view.ActionBarView"; + } else { + className = "com.actionbarsherlock.internal.view.ActionBarView"; + } + assertNull(findViewByClassName(getActivity().getWindow().getDecorView(), className)); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0039.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0039.java new file mode 100644 index 000000000..29cd25e67 --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0039.java @@ -0,0 +1,39 @@ +package com.actionbarsherlock.tests.runner; + +import com.actionbarsherlock.internal.view.menu.MenuBuilder; +import android.content.Context; +import android.test.AndroidTestCase; +import android.view.Menu; + +public class TestIssue0039 extends AndroidTestCase { + private static final int ITEM_A_ID = 1; + private static final int ITEM_B_ID = 2; + private static final int ITEM_C_ID = 3; + private static final int ITEM_D_ID = 4; + private static final int ITEM_E_ID = 5; + + public void testMenuInflationOrderAndCategory() throws Exception { + Menu supportMenu = new MenuBuilder(getContext()); + addMenuItems(supportMenu); + + Class nativeClass = Class.forName("com.android.internal.view.menu.MenuBuilder"); + Menu nativeMenu = (Menu)nativeClass.getConstructor(Context.class).newInstance(getContext()); + addMenuItems(nativeMenu); + + assertEquals(nativeMenu.size(), supportMenu.size()); + assertEquals(5, supportMenu.size()); + assertEquals(nativeMenu.getItem(0).getItemId(), supportMenu.getItem(0).getItemId()); + assertEquals(nativeMenu.getItem(1).getItemId(), supportMenu.getItem(1).getItemId()); + assertEquals(nativeMenu.getItem(2).getItemId(), supportMenu.getItem(2).getItemId()); + assertEquals(nativeMenu.getItem(3).getItemId(), supportMenu.getItem(3).getItemId()); + assertEquals(nativeMenu.getItem(4).getItemId(), supportMenu.getItem(4).getItemId()); + } + + private static void addMenuItems(Menu target) { + target.add(Menu.CATEGORY_SECONDARY, ITEM_A_ID, 0, "A"); + target.add(Menu.NONE , ITEM_B_ID, 0, "B"); + target.add(Menu.NONE , ITEM_C_ID, 0, "C"); + target.add(Menu.CATEGORY_SECONDARY, ITEM_D_ID, 1, "D"); + target.add(Menu.CATEGORY_SECONDARY, ITEM_E_ID, 0, "E"); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0042.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0042.java new file mode 100644 index 000000000..585b8d668 --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0042.java @@ -0,0 +1,26 @@ +package com.actionbarsherlock.tests.runner; + +import android.test.suitebuilder.annotation.Smoke; +import com.actionbarsherlock.tests.app.Issue0042; + +public class TestIssue0042 extends BaseTestCase { + public TestIssue0042() { + super(Issue0042.class); + } + + @Smoke + public void testActivitySetTitleWithString() throws InterruptedException { + getActivity().setTitleString(); + CharSequence actual = getActivity().getSupportActionBarTitle(); + assertNotNull(actual); + assertEquals(Issue0042.TITLE, actual); + } + + @Smoke + public void testActivitySetTitleWithResource() throws InterruptedException { + String expected = getActivity().setTitleResource(); + CharSequence actual = getActivity().getSupportActionBarTitle(); + assertNotNull(actual); + assertEquals(expected, actual); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0045.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0045.java new file mode 100644 index 000000000..075fb2246 --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0045.java @@ -0,0 +1,17 @@ +package com.actionbarsherlock.tests.runner; + +import android.test.suitebuilder.annotation.Smoke; +import com.actionbarsherlock.tests.app.Issue0045; + +public class TestIssue0045 extends BaseTestCase { + public TestIssue0045() { + super(Issue0045.class); + } + + @Smoke + public void testMenuFindItemDoesNotError() throws InterruptedException { + Object menuItem = getActivity().getMenuItemSubMenu(); + assertNotNull(menuItem); + assertTrue(menuItem instanceof android.support.v4.view.SubMenu); + } +} diff --git a/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0048.java b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0048.java new file mode 100644 index 000000000..eab386588 --- /dev/null +++ b/actionbarsherlock/test/runner/src/com/actionbarsherlock/tests/runner/TestIssue0048.java @@ -0,0 +1,18 @@ +package com.actionbarsherlock.tests.runner; + +import android.support.v4.view.Menu; +import android.test.suitebuilder.annotation.Smoke; +import com.actionbarsherlock.tests.app.Issue0048; + +public class TestIssue0048 extends BaseTestCase { + public TestIssue0048() { + super(Issue0048.class); + } + + @Smoke + public void testViewPagerOnlyCurrentMenu() throws InterruptedException { + Menu menu = getActivity().getMenu(); + assertNotNull(menu); + assertEquals(1, menu.size()); + } +} diff --git a/actionbarsherlock/update-version.py b/actionbarsherlock/update-version.py new file mode 100644 index 000000000..a80af1570 --- /dev/null +++ b/actionbarsherlock/update-version.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +import os +import re +from datetime import date + +repo = os.path.dirname(os.path.realpath(__file__)) +changelog = os.path.join(repo, 'CHANGELOG.md') + +code = 'android:versionCode="%s"' +name = 'android:versionName="%s"' +in_code = code % r'(\d+)' +in_name = name % r'([^"]+)' +new_code = None +new_name = None + +# Update manifest files +for dirpath, dirnames, filenames in os.walk(repo): + for filename in filenames: + if filename == 'AndroidManifest.xml': + filepath = os.path.join(dirpath, filename) + with open(filepath) as f: + contents = f.read() + if new_code is None: + print('Current version code: ' + re.search(in_code, contents).group(1)) + new_code = raw_input('New version code: ') + print('Current version name: ' + re.search(in_name, contents).group(1)) + new_name = raw_input('New version name: ') + contents = re.sub(in_code, code % new_code, contents) + contents = re.sub(in_name, name % new_name, contents) + with open(filepath, 'w') as f: + f.write(contents) + +# Update change log +with open(changelog) as f: + contents = f.read() +if 'In Development' in contents: + contents = contents.replace('In Development', date.today().strftime('%Y-%m-%d')) + with open(changelog, 'w') as f: + f.write(contents) diff --git a/actionbarsherlock/website/resources/.gitignore b/actionbarsherlock/website/resources/.gitignore new file mode 100644 index 000000000..0815732fb --- /dev/null +++ b/actionbarsherlock/website/resources/.gitignore @@ -0,0 +1,5 @@ +_site + +#Maven site plugin generated folders +css +images diff --git a/actionbarsherlock/website/resources/CNAME b/actionbarsherlock/website/resources/CNAME new file mode 100644 index 000000000..87a75d963 --- /dev/null +++ b/actionbarsherlock/website/resources/CNAME @@ -0,0 +1 @@ +actionbarsherlock.com diff --git a/actionbarsherlock/website/resources/_config.yml b/actionbarsherlock/website/resources/_config.yml new file mode 100644 index 000000000..d237810f7 --- /dev/null +++ b/actionbarsherlock/website/resources/_config.yml @@ -0,0 +1,7 @@ +url: "http://actionbarsherlock.com" +pygments: true +markdown: kramdown +auto: true + +kramdown: + entity_output: symbolic diff --git a/actionbarsherlock/website/resources/_layouts/default.html b/actionbarsherlock/website/resources/_layouts/default.html new file mode 100644 index 000000000..4fd89c98d --- /dev/null +++ b/actionbarsherlock/website/resources/_layouts/default.html @@ -0,0 +1,78 @@ + + + + + + + + + + ActionBarSherlock - {{page.title}} + + + + + + + + + + + + + + +
+ + +{{content}} + + +
+ + diff --git a/actionbarsherlock/website/resources/development.html b/actionbarsherlock/website/resources/development.html new file mode 100644 index 000000000..7e373a4c4 --- /dev/null +++ b/actionbarsherlock/website/resources/development.html @@ -0,0 +1,33 @@ +--- +title: Development +layout: default +--- + + +
+
+

Development

+

The ActionBarSherlock library along with its sample applications are all built using Apache Maven. Maven will automate the process of compiling and assembling the apklib and the apk for each project.

+

Using Maven is NOT a requirement for contributing to development. It is merely used for automated testing and releasing of the libary and sample applications.

+ +

IDE

+

Even though the project uses Maven, each folder within the repository still maintains the standard Android project layout. By specifying the desired folder as an existing source for a new Android project, you should be able to successfully import the sources.

+

The library should be created as a project first. Any sample applications you also wish to create projects for can then be created.

+ +

Prerequisites

+

Maven compilation requires that you have installed the Android SDK into your local maven repository. This can be done automatically by the SDK Deployer.

+

Follow the instructions on setting up the SDK Deployer. Then, execute the following command in the SDK Deployer folder:

+

mvn install -P 3.1

+

This will install the Android SDK 3.1 into the local repository.

+ +

Compiling

+

Once the prerequisites have been successfully installed, building is as easy as running mvn clean package. The apklib for the library will be located in the library/target/ folder.

+

An apk for each of the samples are their respective target/ folders.

+
+
+

Getting The Code

+

The source code repository is hosted on GitHub at JakeWharton/ActionBarSherlock.

+

The GitHub project is also the location of the issue tracker as well as the source code to this website.

+

View project on GitHub

+
+
\ No newline at end of file diff --git a/actionbarsherlock/website/resources/download.html b/actionbarsherlock/website/resources/download.html new file mode 100644 index 000000000..0d73a023f --- /dev/null +++ b/actionbarsherlock/website/resources/download.html @@ -0,0 +1,160 @@ +--- +title: Download +layout: default +--- + + + + +
+
+

Download

+

Version (loading)(loading) (change log)

+

Zip Tarball

+ +

Plugins

+
+
(loading)
+
+ +

Sample Applications

+
+
(loading)
+
+ +

Including In Your Project

+

There are two ways to leverage ActionBarSherlock in your projects:

+
    +
  1. If you’re using the Eclipse Development Environment with the ADT plugin version 0.9.7 or greater you can include this as a library project. Create a new Android project in Eclipse using the library/ folder as the existing source. Then, in your project properties, add the created project under the ‘Libraries’ section of the ‘Android’ category.
  2. +
  3. +

    If you use maven to build your Android project you can simply add a dependency for this library.

    +
    +<dependency>
    +  <groupId>com.actionbarsherlock</groupId>
    +  <artifactId>library</artifactId>
    +  <version>3.0.2</version>
    +  <type>apklib</type>
    +</dependency>
    +
    +

    Make sure you also include r.jakewharton.com/maven/release/ in the repositories section of your pom.xml.

    +
  4. +
+

Note: If you were previously using the Android compatability library you must remove its .jar. ActionBarSherlock is built on top of the compatability library and comes bundled with its classes.

+
+
+

Release History

+
+
(loading)
+
+

View all »

+
+
+ + diff --git a/actionbarsherlock/website/resources/faq.html b/actionbarsherlock/website/resources/faq.html new file mode 100644 index 000000000..6149793d2 --- /dev/null +++ b/actionbarsherlock/website/resources/faq.html @@ -0,0 +1,73 @@ +--- +title: Merchandise and Donations +layout: default +--- + + +
+
+

Frequently Asked Questions

+

Have a question that is not listed on this page? See the "Direct Contact" section of the support page.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
What action bar library is used on pre-3.0 devices? +

The widget is a custom implementation that was purpose-built to mimic the API, functionality, and look of the native action bar on 3.0+ devices.

+

The basis for the widget was work done on Johan Nilsson's Android-ActionBar library. The first two versions of ActionBarSherlock used his library directly for support on pre-3.0 devices and without his work the current version of the library would not be possible.

+
What API level should I target if I want to use the library?The library itself must be built against Android 3.2 (API level 13). Your project should be built using the latest version of the SDK as possible (it is best to try and use the same version as the library). This is not a requirement, however, and you may build against any version of the SDK that is Android 3.0 (API level 11) or newer.
Why do I have to target at least API level 11 or newer?Targetting API level 11 or newer will cause Android to automatically add the native action bar when run on devices using Android 3.0+. Since you will be compiling against new APIs but your app will likely be run on devices with older versions of Android extra care must be taken to either avoid using or properly check and call any methods that were introduced after your minimum SDK version.
Why do action modes not work on pre-3.0 devices?Action mode support is still being worked on for pre-3.0 devices and will be released with a future version of the library.
Why doesn't the logo defined on the activity/application entry in the manifest get displayed?Despite there being support for loading the logo for activities and applications since API level 9, the underlying implementation in Android is broken and does not work at all. In order to specify a logo on these platforms you should use the abLogo attribute in a custom theme.
List and tab navigation hide the title on pre-3.0 devices. Can this be changed?Due to the relatively small space to display items on phones, this cannot currently be changed. Future versions of the library will behave more like the native action bar on larger displays (e.g., Nook Color, Archos 101).
Can you add functionality *X* to the action bar?As a general rule, no. If the native action bar on Android 3.0+ does not support it then it will not be supported by this library.
Why is ActionBarSherlock a library project whereas the original compatibility library is only a .jar?ActionBarWatson, the custom action bar implementation within ActionBarSherlock for pre-3.0 devices, relies on styles, themes, layouts, and drawables in order to display properly. Due to the limitations of Android and .jar files, it can not be accomplished any other way.
I found a bug, what should I do?See the support page and create an issue on GitHub, post to the Google Group, or contact me directly. Or do all three! The worst thing you can do is to not report it. Please try to include as much information as possible in your report.
What causes the following error: Binary XML file line #8: You must supply a layout_height attribute?You application or activity is not using a theme from the application. See the "Parent Themes" section of the theming page.
Do I need to include the .jar file for the Android compatability library when using ActionBarSherlock?No. ActionBarSherlock is built on top of the Android compatability library and comes bundled with its class files. Including the .jar will result in a very large number of compile-time errors.
I use progaurd. Are there any lines I should include to ensure functionality of the library? +

Add the following lines:

+
+-keep class android.support.v4.app.** { *; }
+-keep interface android.support.v4.app.** { *; }
+-keep class com.actionbarsherlock.internal.** { *; }
+-keep interface com.actionbarsherlock.internal.** { *; }
+					
+
+
+
diff --git a/actionbarsherlock/website/resources/favicon.ico b/actionbarsherlock/website/resources/favicon.ico new file mode 100644 index 000000000..0b9f03ed0 Binary files /dev/null and b/actionbarsherlock/website/resources/favicon.ico differ diff --git a/actionbarsherlock/website/resources/humans.txt b/actionbarsherlock/website/resources/humans.txt new file mode 100644 index 000000000..9eae994e4 --- /dev/null +++ b/actionbarsherlock/website/resources/humans.txt @@ -0,0 +1,25 @@ +/* TEAM */ + + Name: Jake Wharton + Site: http://jakewharton.com + EMail: jakewharton@gmail.com + Twitter: @JakeWharton + Location: Pittsburgh, PA + + + +/* THANKS */ + + Name: Johan Nilsson + Site: http://markupartist.com/ + + + +/* SITE */ + + Last update: 2011-06-15 + Language: English + Doctype: HTML5 + Standards: HTML5, CSS2, Markdown + Components: jQuery, Colorbox (jQuery plugin) + Software: nano, jekyll, GIMP diff --git a/actionbarsherlock/website/resources/index.html b/actionbarsherlock/website/resources/index.html new file mode 100644 index 000000000..0c996acef --- /dev/null +++ b/actionbarsherlock/website/resources/index.html @@ -0,0 +1,173 @@ + + + + + + + + + + ActionBarSherlock - Home + + + + + + + + + + + + + +
+
+
+ +

ActionBarSherlock is an extension of the compatibility library designed to facilitate the use of the action bar design pattern across all versions of Android with a single API.

+
Donations & Merhandise
+

The library will automatically use the native action bar when available or will automatically wrap a custom implementation around your layouts. This allows you to easily develop an application with an action bar for every version of Android back through 1.6.

+
+

Download : Zip Tarball More...

+
+
+
+ +
+
+ +
+
+

Usage

+

Interaction with the action bar is provided through a single API by calling getSupportActionBar(). The methods provided by this interface mirror those of the native action bar exactly.

+

Enabling support is as simple as extending your activities from FragmentActivity and declaring a theme in your manifest file.

+

Learn More »

+
+
+

Theming

+

A single theme can be used to style the action bar to match the look and feel of your application.

+

If you need to customize the look for a phone or tablet, Android's powerful resource filtering can still be leveraged.

+

Learn More »

+
+
+

Samples

+

View screenshots and descriptions of the three sample applications which are bundled with the project as well as open source applications who chose to implement the library.

+

Each project links to its source so you can investigate their code for reference.

+

Learn More »

+
+
+

Resources

+
+
Frequently Asked Questions
+
Common pitfals and their solutions.
+
Help & Support
+
Places to go when you need assitance with implementation.
+
Development
+
Contribute to the libary.
+
Merchandise & Donations
+
Show support by purchasing a shirt with our mascot and/or donate directly!
+
+
+
+ +
+
+

Featured Implementations

+
+
+
+ +
+

FriendCaster

+

FriendCaster for Facebook gives you the best Facebook experience on Android devices!

+ +
+
+
+ +
+

RateBeer

+

Mobile interface to the world's largest source for information on craft beer: RateBeer.

+ +
+
+
+
+
+ +
+

SeriesGuide Show Manager

+

Manage the progress of (re-)watching your favorite TV shows. Keep track of which episodes you still have to watch or which ones are upcoming.

+ +
+
+
+ +
+

Folder Organizer

+

Group items according to labels and create fully customizable folders and widgets. You can organize apps, bookmarks, contacts and shortcuts.

+ +
+
+
+
+ + +
+ + diff --git a/actionbarsherlock/website/resources/merch-and-donations.html b/actionbarsherlock/website/resources/merch-and-donations.html new file mode 100644 index 000000000..30cd68fb0 --- /dev/null +++ b/actionbarsherlock/website/resources/merch-and-donations.html @@ -0,0 +1,36 @@ +--- +title: Merchandise and Donations +layout: default +--- + + +
+
+

Merchandise

+

Purchase merchandise containing the ActionBarSherlock mascot! A small portion of the proceeds will go to benefit development of the library.

+

actionbarsherlock.spreadshirt.com

+

Merchandise

+
+
+

Flattr

+

The Flattr service is available for those users who would like to show a continued support of the project.

+

+

For more information on how Flattr works, please visit their website.

+
+
+

PayPal

+

PayPal is available for one-time contributions of your choice.

+
+ + +

+ + +

+
+

Due to the fees that PayPal charges, please donate no less than $1.

+
+
+

Your monetary support is greatly appreciated and will be used toward the costs assocaited with developing this library (e.g., GitHub subscription, designer commisioning, etc.).

+
+
diff --git a/actionbarsherlock/website/resources/robots.txt b/actionbarsherlock/website/resources/robots.txt new file mode 100644 index 000000000..cb4bc85f9 --- /dev/null +++ b/actionbarsherlock/website/resources/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Sitemap: http://actionbarsherlock.com/sitemap.xml diff --git a/actionbarsherlock/website/resources/samples.html b/actionbarsherlock/website/resources/samples.html new file mode 100644 index 000000000..7b4202258 --- /dev/null +++ b/actionbarsherlock/website/resources/samples.html @@ -0,0 +1,82 @@ +--- +title: Samples +layout: default +--- + + + + +
+
+

Library Samples

+

The library comes with three samples, each of which demonstrate a different use case of ActionBarSherlock.

+

A compiled .apk for each sample can be downloaded under the "Sample Applications" section of the download page. You can obtain the source code for each in the samples/ directory of the repository.

+ +

Demo

+

The demo sample is a showcase of the functionality of the library (most notably, the action bar). There are several demos of different functionality but the most important is the feature toggles. A set of buttons make up the layout of this activity which allow you to toggle virtually all of the display-related settings of the action bar to observe how it behaves on various device configurations.

+ + +

Shakespeare

+

The shakespeare sample was used to introduce fragments on the Android Blog and is now part of the SDK's API Demos. The demo has been modified slightly to show how ActionBarSherlock operates as an extension of the compatibility library.

+ + +

Styled

+

The styled sample is an application developed by Nick Butcher and posted on the Android Developers Blog as an example of how to use theming capabilities of Android to allow the action bar to match the style of your application.

+ +
+
+

Open-Source Apps

+

The following are open-source applications which implement ActionBarSherlock. These should provide a more realistic view of what implementation is like provide solutions for common implementation problems.

+

If your application uses this library and is open source please contact Jake Wharton to have it added to this list.

+ +

SeriesGuide Show Manager

+

Manage the progress of (re-)watching your favorite TV shows. Keep track of which episodes you still have to watch or which ones are upcoming.

+

Market · GitHub

+ + +

RateBeer

+

Mobile interface to the world's largest source for information on craft beer: RateBeer

+

Market · Google Code

+ +

VampiDroid

+

Android app to search and organize cards of Jyhadâ„¢, Vampire: The Eternal Struggleâ„¢

+

Market · Google Code

+
+
diff --git a/actionbarsherlock/website/resources/sitemap.xml b/actionbarsherlock/website/resources/sitemap.xml new file mode 100644 index 000000000..cdbe22cf0 --- /dev/null +++ b/actionbarsherlock/website/resources/sitemap.xml @@ -0,0 +1,60 @@ + + + + + http://actionbarsherlock.com/ + 2011-07-16 + weekly + 1.0 + + + https://actionbarsherlock.com/download.html + 2011-07-16 + weekly + 0.9 + + + https://actionbarsherlock.com/usage.html + 2011-07-16 + weekly + 0.8 + + + https://actionbarsherlock.com/theming.html + 2011-07-16 + weekly + 0.5 + + + https://actionbarsherlock.com/samples.html + 2011-07-16 + weekly + 0.7 + + + https://actionbarsherlock.com/faq.html + 2011-07-16 + weekly + 0.4 + + + https://actionbarsherlock.com/support.html + 2011-07-16 + weekly + 0.6 + + + https://actionbarsherlock.com/development.html + 2011-07-16 + weekly + 0.3 + + + https://actionbarsherlock.com/merch-and-donations.html + 2011-07-16 + weekly + 0.5 + + diff --git a/actionbarsherlock/website/resources/static/1120.css b/actionbarsherlock/website/resources/static/1120.css new file mode 100644 index 000000000..2d8ebca78 --- /dev/null +++ b/actionbarsherlock/website/resources/static/1120.css @@ -0,0 +1,63 @@ +/* CSS Reset */ + +html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,address,cite,code,del,dfn,em,img,ins,q,small,strong,sub,sup,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{border:0;margin:0;padding:0}article,aside,figure,figure img,figcaption,hgroup,footer,header,nav,section,video,object{display:block}a img{border:0}figure{position:relative}figure img{width:100%} + + +/* ==================================================================================================================== */ +/* ! The 1120px Grid V1 by Jake Wharton \ http://jakewharton.com \ http://twitter.com/JakeWharton */ +/* ! */ +/* ! BASED OFF OF: */ +/* ! The 1140px Grid V2 by Andy Taylor \ http://cssgrid.net \ http://www.twitter.com/andytlr \ http://www.andytlr.com */ +/* ==================================================================================================================== */ + +.container { padding-left: 20px; padding-right: 20px; } +.row { width: 100%; max-width: 1120px; margin: 0 auto; overflow: hidden; } +.row > div { float: left; min-height: 1px; } + +.row .fourth { width: 250px; margin-right: 40px; } +.row .half { width: 540px; margin-right: 40px; } +.row .three-fourths { width: 790px; margin-right: 40px; } +.row .whole { width: 100%; } + +.row .last { margin-right: 0 !important; } + + +img, object, embed { max-width: 100%; } +img { height: auto; } + + +/* Smaller screens */ + +@media only screen and (max-width: 1159px) { + /*body { font-size: 0.8em; line-height: 1.5em; }*/ + + .row .fourth { width: 22.375%; margin-right: 3.5%; } + .row .half { width: 48.25%; margin-right: 3.5%; } + .row .three-fourths { width: 71.5%; margin-right: 3.5%;} +} + + +/* Mobile */ + +@media handheld, only screen and (max-width: 767px) { + body { + font-size: 16px; + -webkit-text-size-adjust: none; + } + .row, body, .container { + width: 100%; + min-width: 0; + margin-left: 0px; + margin-right: 0px; + padding-left: 0px; + padding-right: 0px; + } + .row .fourth, .row .half, .row .three-fourths, .row .whole { + width: auto; + float: none; + margin-left: 0px; + margin-right: 0px; + padding-left: 20px; + padding-right: 20px; + } +} \ No newline at end of file diff --git a/actionbarsherlock/website/resources/static/apps/com.abcOrganizer.png b/actionbarsherlock/website/resources/static/apps/com.abcOrganizer.png new file mode 100644 index 000000000..d96f593e4 Binary files /dev/null and b/actionbarsherlock/website/resources/static/apps/com.abcOrganizer.png differ diff --git a/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide.png b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide.png new file mode 100644 index 000000000..926e618e0 Binary files /dev/null and b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide.png differ diff --git a/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_01_2.3.4.png b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_01_2.3.4.png new file mode 100644 index 000000000..8ac68259e Binary files /dev/null and b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_01_2.3.4.png differ diff --git a/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_01_2.3.4.thumb.png b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_01_2.3.4.thumb.png new file mode 100644 index 000000000..a43e3c624 Binary files /dev/null and b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_01_2.3.4.thumb.png differ diff --git a/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_01_3.0.png b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_01_3.0.png new file mode 100644 index 000000000..47be7f7b4 Binary files /dev/null and b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_01_3.0.png differ diff --git a/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_01_3.0.thumb.png b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_01_3.0.thumb.png new file mode 100644 index 000000000..15807a62e Binary files /dev/null and b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_01_3.0.thumb.png differ diff --git a/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_02_2.3.4.png b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_02_2.3.4.png new file mode 100644 index 000000000..976e13459 Binary files /dev/null and b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_02_2.3.4.png differ diff --git a/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_02_2.3.4.thumb.png b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_02_2.3.4.thumb.png new file mode 100644 index 000000000..7d4d5f17c Binary files /dev/null and b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_02_2.3.4.thumb.png differ diff --git a/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_02_3.0.png b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_02_3.0.png new file mode 100644 index 000000000..9e1932e4f Binary files /dev/null and b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_02_3.0.png differ diff --git a/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_02_3.0.thumb.png b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_02_3.0.thumb.png new file mode 100644 index 000000000..1b14a5c6d Binary files /dev/null and b/actionbarsherlock/website/resources/static/apps/com.battlelancer.seriesguide_02_3.0.thumb.png differ diff --git a/actionbarsherlock/website/resources/static/apps/com.ratebeer.android.png b/actionbarsherlock/website/resources/static/apps/com.ratebeer.android.png new file mode 100644 index 000000000..4f5e3ac07 Binary files /dev/null and b/actionbarsherlock/website/resources/static/apps/com.ratebeer.android.png differ diff --git a/actionbarsherlock/website/resources/static/apps/uk.co.senab.blueNotifyFree.png b/actionbarsherlock/website/resources/static/apps/uk.co.senab.blueNotifyFree.png new file mode 100644 index 000000000..f8bccc663 Binary files /dev/null and b/actionbarsherlock/website/resources/static/apps/uk.co.senab.blueNotifyFree.png differ diff --git a/actionbarsherlock/website/resources/static/black_linen_v2.png b/actionbarsherlock/website/resources/static/black_linen_v2.png new file mode 100644 index 000000000..d125b4b8a Binary files /dev/null and b/actionbarsherlock/website/resources/static/black_linen_v2.png differ diff --git a/actionbarsherlock/website/resources/static/border.png b/actionbarsherlock/website/resources/static/border.png new file mode 100644 index 000000000..f463a10d8 Binary files /dev/null and b/actionbarsherlock/website/resources/static/border.png differ diff --git a/actionbarsherlock/website/resources/static/controls.png b/actionbarsherlock/website/resources/static/controls.png new file mode 100644 index 000000000..925717661 Binary files /dev/null and b/actionbarsherlock/website/resources/static/controls.png differ diff --git a/actionbarsherlock/website/resources/static/css3-mediaqueries.js b/actionbarsherlock/website/resources/static/css3-mediaqueries.js new file mode 100644 index 000000000..76bc26048 --- /dev/null +++ b/actionbarsherlock/website/resources/static/css3-mediaqueries.js @@ -0,0 +1,779 @@ +if(typeof Object.create!=="function"){ +Object.create=function(o){ +function F(){ +}; +F.prototype=o; +return new F(); +}; +} +var ua={toString:function(){ +return navigator.userAgent; +},test:function(s){ +return this.toString().toLowerCase().indexOf(s.toLowerCase())>-1; +}}; +ua.version=(ua.toString().toLowerCase().match(/[\s\S]+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1]; +ua.webkit=ua.test("webkit"); +ua.gecko=ua.test("gecko")&&!ua.webkit; +ua.opera=ua.test("opera"); +ua.ie=ua.test("msie")&&!ua.opera; +ua.ie6=ua.ie&&document.compatMode&&typeof document.documentElement.style.maxHeight==="undefined"; +ua.ie7=ua.ie&&document.documentElement&&typeof document.documentElement.style.maxHeight!=="undefined"&&typeof XDomainRequest==="undefined"; +ua.ie8=ua.ie&&typeof XDomainRequest!=="undefined"; +var domReady=function(){ +var _1=[]; +var _2=function(){ +if(!arguments.callee.done){ +arguments.callee.done=true; +for(var i=0;i<_1.length;i++){ +_1[i](); +} +} +}; +if(document.addEventListener){ +document.addEventListener("DOMContentLoaded",_2,false); +} +if(ua.ie){ +(function(){ +try{ +document.documentElement.doScroll("left"); +} +catch(e){ +setTimeout(arguments.callee,50); +return; +} +_2(); +})(); +document.onreadystatechange=function(){ +if(document.readyState==="complete"){ +document.onreadystatechange=null; +_2(); +} +}; +} +if(ua.webkit&&document.readyState){ +(function(){ +if(document.readyState!=="loading"){ +_2(); +}else{ +setTimeout(arguments.callee,10); +} +})(); +} +window.onload=_2; +return function(fn){ +if(typeof fn==="function"){ +_1[_1.length]=fn; +} +return fn; +}; +}(); +var cssHelper=function(){ +var _3={BLOCKS:/[^\s{][^{]*\{(?:[^{}]*\{[^{}]*\}[^{}]*|[^{}]*)*\}/g,BLOCKS_INSIDE:/[^\s{][^{]*\{[^{}]*\}/g,DECLARATIONS:/[a-zA-Z\-]+[^;]*:[^;]+;/g,RELATIVE_URLS:/url\(['"]?([^\/\)'"][^:\)'"]+)['"]?\)/g,REDUNDANT_COMPONENTS:/(?:\/\*([^*\\\\]|\*(?!\/))+\*\/|@import[^;]+;)/g,REDUNDANT_WHITESPACE:/\s*(,|:|;|\{|\})\s*/g,MORE_WHITESPACE:/\s{2,}/g,FINAL_SEMICOLONS:/;\}/g,NOT_WHITESPACE:/\S+/g}; +var _4,_5=false; +var _6=[]; +var _7=function(fn){ +if(typeof fn==="function"){ +_6[_6.length]=fn; +} +}; +var _8=function(){ +for(var i=0;i<_6.length;i++){ +_6[i](_4); +} +}; +var _9={}; +var _a=function(n,v){ +if(_9[n]){ +var _b=_9[n].listeners; +if(_b){ +for(var i=0;i<_b.length;i++){ +_b[i](v); +} +} +} +}; +var _c=function(_d,_e,_f){ +if(ua.ie&&!window.XMLHttpRequest){ +window.XMLHttpRequest=function(){ +return new ActiveXObject("Microsoft.XMLHTTP"); +}; +} +if(!XMLHttpRequest){ +return ""; +} +var r=new XMLHttpRequest(); +try{ +r.open("get",_d,true); +r.setRequestHeader("X_REQUESTED_WITH","XMLHttpRequest"); +} +catch(e){ +_f(); +return; +} +var _10=false; +setTimeout(function(){ +_10=true; +},5000); +document.documentElement.style.cursor="progress"; +r.onreadystatechange=function(){ +if(r.readyState===4&&!_10){ +if(!r.status&&location.protocol==="file:"||(r.status>=200&&r.status<300)||r.status===304||navigator.userAgent.indexOf("Safari")>-1&&typeof r.status==="undefined"){ +_e(r.responseText); +}else{ +_f(); +} +document.documentElement.style.cursor=""; +r=null; +} +}; +r.send(""); +}; +var _11=function(_12){ +_12=_12.replace(_3.REDUNDANT_COMPONENTS,""); +_12=_12.replace(_3.REDUNDANT_WHITESPACE,"$1"); +_12=_12.replace(_3.MORE_WHITESPACE," "); +_12=_12.replace(_3.FINAL_SEMICOLONS,"}"); +return _12; +}; +var _13={mediaQueryList:function(s){ +var o={}; +var idx=s.indexOf("{"); +var lt=s.substring(0,idx); +s=s.substring(idx+1,s.length-1); +var mqs=[],rs=[]; +var qts=lt.toLowerCase().substring(7).split(","); +for(var i=0;i-1&&_23.href&&_23.href.length!==0&&!_23.disabled){ +_1f[_1f.length]=_23; +} +} +if(_1f.length>0){ +var c=0; +var _24=function(){ +c++; +if(c===_1f.length){ +_20(); +} +}; +var _25=function(_26){ +var _27=_26.href; +_c(_27,function(_28){ +_28=_11(_28).replace(_3.RELATIVE_URLS,"url("+_27.substring(0,_27.lastIndexOf("/"))+"/$1)"); +_26.cssHelperText=_28; +_24(); +},_24); +}; +for(i=0;i<_1f.length;i++){ +_25(_1f[i]); +} +}else{ +_20(); +} +}; +var _29={mediaQueryLists:"array",rules:"array",selectors:"object",declarations:"array",properties:"object"}; +var _2a={mediaQueryLists:null,rules:null,selectors:null,declarations:null,properties:null}; +var _2b=function(_2c,v){ +if(_2a[_2c]!==null){ +if(_29[_2c]==="array"){ +return (_2a[_2c]=_2a[_2c].concat(v)); +}else{ +var c=_2a[_2c]; +for(var n in v){ +if(v.hasOwnProperty(n)){ +if(!c[n]){ +c[n]=v[n]; +}else{ +c[n]=c[n].concat(v[n]); +} +} +} +return c; +} +} +}; +var _2d=function(_2e){ +_2a[_2e]=(_29[_2e]==="array")?[]:{}; +for(var i=0;i<_4.length;i++){ +_2b(_2e,_4[i].cssHelperParsed[_2e]); +} +return _2a[_2e]; +}; +domReady(function(){ +var els=document.body.getElementsByTagName("*"); +for(var i=0;i=_44)||(max&&_46<_44)||(!min&&!max&&_46===_44)); +}else{ +return false; +} +}else{ +return _46>0; +} +}else{ +if("device-height"===_41.substring(l-13,l)){ +_47=screen.height; +if(_42!==null){ +if(_43==="length"){ +return ((min&&_47>=_44)||(max&&_47<_44)||(!min&&!max&&_47===_44)); +}else{ +return false; +} +}else{ +return _47>0; +} +}else{ +if("width"===_41.substring(l-5,l)){ +_46=document.documentElement.clientWidth||document.body.clientWidth; +if(_42!==null){ +if(_43==="length"){ +return ((min&&_46>=_44)||(max&&_46<_44)||(!min&&!max&&_46===_44)); +}else{ +return false; +} +}else{ +return _46>0; +} +}else{ +if("height"===_41.substring(l-6,l)){ +_47=document.documentElement.clientHeight||document.body.clientHeight; +if(_42!==null){ +if(_43==="length"){ +return ((min&&_47>=_44)||(max&&_47<_44)||(!min&&!max&&_47===_44)); +}else{ +return false; +} +}else{ +return _47>0; +} +}else{ +if("device-aspect-ratio"===_41.substring(l-19,l)){ +return _43==="aspect-ratio"&&screen.width*_44[1]===screen.height*_44[0]; +}else{ +if("color-index"===_41.substring(l-11,l)){ +var _48=Math.pow(2,screen.colorDepth); +if(_42!==null){ +if(_43==="absolute"){ +return ((min&&_48>=_44)||(max&&_48<_44)||(!min&&!max&&_48===_44)); +}else{ +return false; +} +}else{ +return _48>0; +} +}else{ +if("color"===_41.substring(l-5,l)){ +var _49=screen.colorDepth; +if(_42!==null){ +if(_43==="absolute"){ +return ((min&&_49>=_44)||(max&&_49<_44)||(!min&&!max&&_49===_44)); +}else{ +return false; +} +}else{ +return _49>0; +} +}else{ +if("resolution"===_41.substring(l-10,l)){ +var res; +if(_45==="dpcm"){ +res=_3d("1cm"); +}else{ +res=_3d("1in"); +} +if(_42!==null){ +if(_43==="resolution"){ +return ((min&&res>=_44)||(max&&res<_44)||(!min&&!max&&res===_44)); +}else{ +return false; +} +}else{ +return res>0; +} +}else{ +return false; +} +} +} +} +} +} +} +} +}; +var _4a=function(mq){ +var _4b=mq.getValid(); +var _4c=mq.getExpressions(); +var l=_4c.length; +if(l>0){ +for(var i=0;i0){ +s[c++]=","; +} +s[c++]=n; +} +} +if(s.length>0){ +_39[_39.length]=cssHelper.addStyle("@media "+s.join("")+"{"+mql.getCssText()+"}",false); +} +}; +var _4e=function(_4f){ +for(var i=0;i<_4f.length;i++){ +_4d(_4f[i]); +} +if(ua.ie){ +document.documentElement.style.display="block"; +setTimeout(function(){ +document.documentElement.style.display=""; +},0); +setTimeout(function(){ +cssHelper.broadcast("cssMediaQueriesTested"); +},100); +}else{ +cssHelper.broadcast("cssMediaQueriesTested"); +} +}; +var _50=function(){ +for(var i=0;i<_39.length;i++){ +cssHelper.removeStyle(_39[i]); +} +_39=[]; +cssHelper.mediaQueryLists(_4e); +}; +var _51=0; +var _52=function(){ +var _53=cssHelper.getViewportWidth(); +var _54=cssHelper.getViewportHeight(); +if(ua.ie){ +var el=document.createElement("div"); +el.style.position="absolute"; +el.style.top="-9999em"; +el.style.overflow="scroll"; +document.body.appendChild(el); +_51=el.offsetWidth-el.clientWidth; +document.body.removeChild(el); +} +var _55; +var _56=function(){ +var vpw=cssHelper.getViewportWidth(); +var vph=cssHelper.getViewportHeight(); +if(Math.abs(vpw-_53)>_51||Math.abs(vph-_54)>_51){ +_53=vpw; +_54=vph; +clearTimeout(_55); +_55=setTimeout(function(){ +if(!_3a()){ +_50(); +}else{ +cssHelper.broadcast("cssMediaQueriesTested"); +} +},500); +} +}; +window.onresize=function(){ +var x=window.onresize||function(){ +}; +return function(){ +x(); +_56(); +}; +}(); +}; +var _57=document.documentElement; +_57.style.marginLeft="-32767px"; +setTimeout(function(){ +_57.style.marginTop=""; +},20000); +return function(){ +if(!_3a()){ +cssHelper.addListener("newStyleParsed",function(el){ +_4e(el.cssHelperParsed.mediaQueryLists); +}); +cssHelper.addListener("cssMediaQueriesTested",function(){ +if(ua.ie){ +_57.style.width="1px"; +} +setTimeout(function(){ +_57.style.width=""; +_57.style.marginLeft=""; +},0); +cssHelper.removeListener("cssMediaQueriesTested",arguments.callee); +}); +_3c(); +_50(); +}else{ +_57.style.marginLeft=""; +} +_52(); +}; +}()); +try{ +document.execCommand("BackgroundImageCache",false,true); +} +catch(e){ +} + diff --git a/actionbarsherlock/website/resources/static/feature.png b/actionbarsherlock/website/resources/static/feature.png new file mode 100644 index 000000000..a15b531e0 Binary files /dev/null and b/actionbarsherlock/website/resources/static/feature.png differ diff --git a/actionbarsherlock/website/resources/static/feature_01.png b/actionbarsherlock/website/resources/static/feature_01.png new file mode 100644 index 000000000..8379aea3e Binary files /dev/null and b/actionbarsherlock/website/resources/static/feature_01.png differ diff --git a/actionbarsherlock/website/resources/static/feature_02.png b/actionbarsherlock/website/resources/static/feature_02.png new file mode 100644 index 000000000..f85f62af5 Binary files /dev/null and b/actionbarsherlock/website/resources/static/feature_02.png differ diff --git a/actionbarsherlock/website/resources/static/feature_03.png b/actionbarsherlock/website/resources/static/feature_03.png new file mode 100644 index 000000000..46eb394ac Binary files /dev/null and b/actionbarsherlock/website/resources/static/feature_03.png differ diff --git a/actionbarsherlock/website/resources/static/icon_tgz.png b/actionbarsherlock/website/resources/static/icon_tgz.png new file mode 100644 index 000000000..b896b2767 Binary files /dev/null and b/actionbarsherlock/website/resources/static/icon_tgz.png differ diff --git a/actionbarsherlock/website/resources/static/icon_web.png b/actionbarsherlock/website/resources/static/icon_web.png new file mode 100644 index 000000000..e4eb1691d Binary files /dev/null and b/actionbarsherlock/website/resources/static/icon_web.png differ diff --git a/actionbarsherlock/website/resources/static/icon_zip.png b/actionbarsherlock/website/resources/static/icon_zip.png new file mode 100644 index 000000000..3b1b54fd4 Binary files /dev/null and b/actionbarsherlock/website/resources/static/icon_zip.png differ diff --git a/actionbarsherlock/website/resources/static/ie.css b/actionbarsherlock/website/resources/static/ie.css new file mode 100644 index 000000000..530c113ed --- /dev/null +++ b/actionbarsherlock/website/resources/static/ie.css @@ -0,0 +1,43 @@ +.onecol { +width: 4.7%; +} + +.twocol { +width: 13.2%; +} + +.threecol { +width: 22.05%; +} + +.fourcol { +width: 30.6%; +} + +.fivecol { +width: 39%; +} + +.sixcol { +width: 48%; +} + +.sevencol { +width: 56.75%; +} + +.eightcol { +width: 61.6%; +} + +.ninecol { +width: 74.05%; +} + +.tencol { +width: 82%; +} + +.elevencol { +width: 91.35%; +} \ No newline at end of file diff --git a/actionbarsherlock/website/resources/static/jquery.colorbox-min.js b/actionbarsherlock/website/resources/static/jquery.colorbox-min.js new file mode 100644 index 000000000..689a007c1 --- /dev/null +++ b/actionbarsherlock/website/resources/static/jquery.colorbox-min.js @@ -0,0 +1,4 @@ +// ColorBox v1.3.17.1 - a full featured, light-weight, customizable lightbox based on jQuery 1.3+ +// Copyright (c) 2011 Jack Moore - jack@colorpowered.com +// Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php +(function(a,b,c){function bc(b){if(!T){O=b,_(a.extend(J,a.data(O,e))),x=a(O),P=0,J.rel!=="nofollow"&&(x=a("."+X).filter(function(){var b=a.data(this,e).rel||this.rel;return b===J.rel}),P=x.index(O),P===-1&&(x=x.add(O),P=x.length-1));if(!R){R=S=!0,q.show();if(J.returnFocus)try{O.blur(),a(O).one(k,function(){try{this.focus()}catch(a){}})}catch(c){}p.css({opacity:+J.opacity,cursor:J.overlayClose?"pointer":"auto"}).show(),J.w=Z(J.initialWidth,"x"),J.h=Z(J.initialHeight,"y"),W.position(0),n&&y.bind("resize."+o+" scroll."+o,function(){p.css({width:y.width(),height:y.height(),top:y.scrollTop(),left:y.scrollLeft()})}).trigger("resize."+o),ba(g,J.onOpen),I.add(C).hide(),H.html(J.close).show()}W.load(!0)}}function bb(){var a,b=f+"Slideshow_",c="click."+f,d,e,g;J.slideshow&&x[1]?(d=function(){E.text(J.slideshowStop).unbind(c).bind(i,function(){if(P"),b.open=!0;f.each(function(){a.data(this,e,a.extend({},a.data(this,e)||d,b)),a(this).addClass(X)}),g=b.open,a.isFunction(g)&&(g=g.call(f)),g&&bc(f[0]);return f},W.init=function(){y=a(c),q=Y().attr({id:e,"class":m?f+(n?"IE6":"IE"):""}),p=Y("Overlay",n?"position:absolute":"").hide(),r=Y("Wrapper"),s=Y("Content").append(z=Y("LoadedContent","width:0; height:0; overflow:hidden"),B=Y("LoadingOverlay").add(Y("LoadingGraphic")),C=Y("Title"),D=Y("Current"),F=Y("Next"),G=Y("Previous"),E=Y("Slideshow").bind(g,bb),H=Y("Close")),r.append(Y().append(Y("TopLeft"),t=Y("TopCenter"),Y("TopRight")),Y(!1,"clear:left").append(u=Y("MiddleLeft"),s,v=Y("MiddleRight")),Y(!1,"clear:left").append(Y("BottomLeft"),w=Y("BottomCenter"),Y("BottomRight"))).children().children().css({"float":"left"}),A=Y(!1,"position:absolute; width:9999px; visibility:hidden; display:none"),a("body").prepend(p,q.append(r,A)),s.children().hover(function(){a(this).addClass("hover")},function(){a(this).removeClass("hover")}).addClass("hover"),K=t.height()+w.height()+s.outerHeight(!0)-s.height(),L=u.width()+v.width()+s.outerWidth(!0)-s.width(),M=z.outerHeight(!0),N=z.outerWidth(!0),q.css({"padding-bottom":K,"padding-right":L}).hide(),F.click(function(){W.next()}),G.click(function(){W.prev()}),H.click(function(){W.close()}),I=F.add(G).add(D).add(E),s.children().removeClass("hover"),p.click(function(){J.overlayClose&&W.close()}),a(b).bind("keydown."+f,function(a){var b=a.keyCode;R&&J.escKey&&b===27&&(a.preventDefault(),W.close()),R&&J.arrowKey&&x[1]&&(b===37?(a.preventDefault(),G.click()):b===39&&(a.preventDefault(),F.click()))})},W.remove=function(){q.add(p).remove(),a("."+X).removeData(e).removeClass(X)},W.position=function(a,c){function g(a){t[0].style.width=w[0].style.width=s[0].style.width=a.style.width,B[0].style.height=B[1].style.height=s[0].style.height=u[0].style.height=v[0].style.height=a.style.height}var d,e=0,f=0;q.hide(),J.fixed&&!n?q.css({position:"fixed"}):(e=y.scrollTop(),f=y.scrollLeft(),q.css({position:"absolute"})),J.right!==!1?f+=Math.max(y.width()-J.w-N-L-Z(J.right,"x"),0):J.left!==!1?f+=Z(J.left,"x"):f+=Math.max(y.width()-J.w-N-L,0)/2,J.bottom!==!1?e+=Math.max(b.documentElement.clientHeight-J.h-M-K-Z(J.bottom,"y"),0):J.top!==!1?e+=Z(J.top,"y"):e+=Math.max(b.documentElement.clientHeight-J.h-M-K,0)/2,q.show(),d=q.width()===J.w+N&&q.height()===J.h+M?0:a,r[0].style.width=r[0].style.height="9999px",q.dequeue().animate({width:J.w+N,height:J.h+M,top:e,left:f},{duration:d,complete:function(){g(this),S=!1,r[0].style.width=J.w+N+L+"px",r[0].style.height=J.h+M+K+"px",c&&c()},step:function(){g(this)}})},W.resize=function(a){if(R){a=a||{},a.width&&(J.w=Z(a.width,"x")-N-L),a.innerWidth&&(J.w=Z(a.innerWidth,"x")),z.css({width:J.w}),a.height&&(J.h=Z(a.height,"y")-M-K),a.innerHeight&&(J.h=Z(a.innerHeight,"y"));if(!a.innerHeight&&!a.height){var b=z.wrapInner("
").children();J.h=b.height(),b.replaceWith(b.children())}z.css({height:J.h}),W.position(J.transition==="none"?0:J.speed)}},W.prep=function(b){function h(b){W.position(b,function(){function o(){m&&q[0].style.removeAttribute("filter")}var b,d,g,h,j=x.length,k,n;!R||(n=function(){clearTimeout(V),B.hide(),ba(i,J.onComplete)},m&&Q&&z.fadeIn(100),C.html(J.title).add(z).show(),j>1?(typeof J.current=="string"&&D.html(J.current.replace(/\{current\}/,P+1).replace(/\{total\}/,j)).show(),F[J.loop||P")[0].src=h),$(d)&&(a("")[0].src=d))):I.hide(),J.iframe?(k=a("