From d335ff70dfd77168b0a581615fc65f979e638415 Mon Sep 17 00:00:00 2001 From: Eric Kok Date: Thu, 21 Mar 2013 14:24:37 +0100 Subject: [PATCH] Use the Crouton library to display errors and success messages. --- core/project.properties | 1 + core/res/values/colors.xml | 2 + .../transdroid/core/gui/DetailsActivity.java | 48 +- .../transdroid/core/gui/TorrentsActivity.java | 92 +- .../core/gui/navigation/NavigationHelper.java | 23 +- external/Crouton/.gitignore | 41 + external/Crouton/LICENSE | 202 +++++ external/Crouton/README.markdown | 199 +++++ external/Crouton/build.gradle | 0 external/Crouton/library/.classpath | 8 + external/Crouton/library/.project | 33 + external/Crouton/library/AndroidManifest.xml | 25 + external/Crouton/library/build.xml | 92 ++ external/Crouton/library/pom.xml | 112 +++ external/Crouton/library/project.properties | 17 + .../android/widget/crouton/Crouton.java | 825 ++++++++++++++++++ .../crouton/DefaultAnimationsBuilder.java | 85 ++ .../widget/crouton/LifecycleCallback.java | 24 + .../android/widget/crouton/Manager.java | 403 +++++++++ .../android/widget/crouton/Style.java | 539 ++++++++++++ external/Crouton/pom.xml | 102 +++ 21 files changed, 2804 insertions(+), 69 deletions(-) create mode 100644 external/Crouton/.gitignore create mode 100644 external/Crouton/LICENSE create mode 100644 external/Crouton/README.markdown create mode 100644 external/Crouton/build.gradle create mode 100644 external/Crouton/library/.classpath create mode 100644 external/Crouton/library/.project create mode 100644 external/Crouton/library/AndroidManifest.xml create mode 100644 external/Crouton/library/build.xml create mode 100644 external/Crouton/library/pom.xml create mode 100644 external/Crouton/library/project.properties create mode 100644 external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/Crouton.java create mode 100644 external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/DefaultAnimationsBuilder.java create mode 100644 external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/LifecycleCallback.java create mode 100644 external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/Manager.java create mode 100644 external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/Style.java create mode 100644 external/Crouton/pom.xml diff --git a/core/project.properties b/core/project.properties index 90b3d809..2c3a9595 100644 --- a/core/project.properties +++ b/core/project.properties @@ -15,3 +15,4 @@ target=android-16 android.library.reference.1=../external/JakeWharton-ActionBarSherlock/library android.library.reference.2=../external/ColorPickerPreference android.library=true +android.library.reference.3=../external/Crouton/library diff --git a/core/res/values/colors.xml b/core/res/values/colors.xml index f4cdf7fa..5203dfd2 100644 --- a/core/res/values/colors.xml +++ b/core/res/values/colors.xml @@ -2,4 +2,6 @@ #8acc12 #7dbb21 + #c81113 + #aada62 diff --git a/core/src/org/transdroid/core/gui/DetailsActivity.java b/core/src/org/transdroid/core/gui/DetailsActivity.java index bf5974ca..f4b81872 100644 --- a/core/src/org/transdroid/core/gui/DetailsActivity.java +++ b/core/src/org/transdroid/core/gui/DetailsActivity.java @@ -17,6 +17,8 @@ import org.transdroid.core.R; import org.transdroid.core.app.settings.ApplicationSettings; import org.transdroid.core.app.settings.ServerSetting; import org.transdroid.core.gui.lists.LocalTorrent; +import org.transdroid.core.gui.log.Log; +import org.transdroid.core.gui.navigation.NavigationHelper; import org.transdroid.daemon.Daemon; import org.transdroid.daemon.IDaemonAdapter; import org.transdroid.daemon.Torrent; @@ -43,12 +45,13 @@ import org.transdroid.daemon.task.StopTask; import android.annotation.TargetApi; import android.content.Intent; import android.os.Build; -import android.widget.Toast; import com.actionbarsherlock.app.SherlockFragmentActivity; -@EActivity(resName="activity_details") -@OptionsMenu(resName="activity_details") +import de.keyboardsurfer.android.widget.crouton.Crouton; + +@EActivity(resName = "activity_details") +@OptionsMenu(resName = "activity_details") public class DetailsActivity extends SherlockFragmentActivity implements TorrentTasksExecutor { @Extra @@ -57,11 +60,13 @@ public class DetailsActivity extends SherlockFragmentActivity implements Torrent // Settings @Bean + protected NavigationHelper navigationHelper; + @Bean protected ApplicationSettings applicationSettings; private IDaemonAdapter currentConnection = null; - + // Details view components - @FragmentById(resName="torrent_details") + @FragmentById(resName = "torrent_details") protected DetailsFragment fragmentDetails; @AfterViews @@ -72,7 +77,7 @@ public class DetailsActivity extends SherlockFragmentActivity implements Torrent finish(); return; } - + // Simple action bar with up, torrent name as title and refresh button getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle(torrent.getName()); @@ -85,31 +90,32 @@ public class DetailsActivity extends SherlockFragmentActivity implements Torrent fragmentDetails.updateTorrent(torrent); refreshTorrentDetails(); refreshTorrentFiles(); - + } - + @TargetApi(Build.VERSION_CODES.HONEYCOMB) @OptionsItem(android.R.id.home) protected void navigateUp() { TorrentsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start(); } - - @OptionsItem(resName="action_refresh") + + @OptionsItem(resName = "action_refresh") protected void refreshScreen() { refreshTorrent(); refreshTorrentDetails(); refreshTorrentFiles(); } - + @Background protected void refreshTorrent() { fragmentDetails.updateIsLoading(true); DaemonTaskResult result = RetrieveTask.create(currentConnection).execute(); fragmentDetails.updateIsLoading(false); if (result instanceof RetrieveTaskSuccessResult) { - onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), ((RetrieveTaskSuccessResult) result).getLabels()); + onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), + ((RetrieveTaskSuccessResult) result).getLabels()); } else { - onCommunicationError((DaemonTaskFailureResult)result); + onCommunicationError((DaemonTaskFailureResult) result); } } @@ -121,7 +127,7 @@ public class DetailsActivity extends SherlockFragmentActivity implements Torrent if (result instanceof GetTorrentDetailsTaskSuccessResult) { onTorrentDetailsRetrieved(((GetTorrentDetailsTaskSuccessResult) result).getTorrentDetails()); } else { - onCommunicationError((DaemonTaskFailureResult)result); + onCommunicationError((DaemonTaskFailureResult) result); } } @@ -133,7 +139,7 @@ public class DetailsActivity extends SherlockFragmentActivity implements Torrent if (result instanceof GetFileListTaskSuccessResult) { onTorrentFilesRetrieved(((GetFileListTaskSuccessResult) result).getFiles()); } else { - onCommunicationError((DaemonTaskFailureResult)result); + onCommunicationError((DaemonTaskFailureResult) result); } } @@ -233,15 +239,15 @@ public class DetailsActivity extends SherlockFragmentActivity implements Torrent @UiThread protected void onTaskSucceeded(DaemonTaskSuccessResult result, int successMessageId, String... messageParams) { - // TODO: Properly report this success - Toast.makeText(this, getString(successMessageId, (Object[]) messageParams), Toast.LENGTH_LONG).show(); + Crouton.showText(this, getString(successMessageId, (Object[]) messageParams), + navigationHelper.CROUTON_INFO_STYLE); } @UiThread protected void onCommunicationError(DaemonTaskFailureResult result) { - // TODO: Properly report this error - Toast.makeText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())), - Toast.LENGTH_LONG).show(); + Log.i(this, result.getException().toString()); + Crouton.showText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())), + navigationHelper.CROUTON_ERROR_STYLE); } @UiThread @@ -261,5 +267,5 @@ public class DetailsActivity extends SherlockFragmentActivity implements Torrent // Update the details fragment with the newly retrieved list of files fragmentDetails.updateTorrentFiles(new ArrayList(torrentFiles)); } - + } diff --git a/core/src/org/transdroid/core/gui/TorrentsActivity.java b/core/src/org/transdroid/core/gui/TorrentsActivity.java index 2c29b439..17e0e8e1 100644 --- a/core/src/org/transdroid/core/gui/TorrentsActivity.java +++ b/core/src/org/transdroid/core/gui/TorrentsActivity.java @@ -21,7 +21,10 @@ import org.transdroid.core.gui.lists.LocalTorrent; import org.transdroid.core.gui.lists.SimpleListItem; import org.transdroid.core.gui.log.Log; import org.transdroid.core.gui.navigation.*; +import org.transdroid.core.gui.navigation.NavigationFilter; +import org.transdroid.core.gui.navigation.NavigationHelper; import org.transdroid.core.gui.navigation.NavigationSelectionView.NavigationFilterManager; +import org.transdroid.core.gui.navigation.StatusType; import org.transdroid.core.gui.settings.*; import org.transdroid.daemon.Daemon; import org.transdroid.daemon.IDaemonAdapter; @@ -51,7 +54,6 @@ import android.os.Build; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.Toast; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.ActionBar.OnNavigationListener; @@ -61,9 +63,12 @@ import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.SherlockListView; import com.actionbarsherlock.widget.SearchView; -@EActivity(resName="activity_torrents") -@OptionsMenu(resName="activity_torrents") -public class TorrentsActivity extends SherlockFragmentActivity implements OnNavigationListener, TorrentTasksExecutor, NavigationFilterManager { +import de.keyboardsurfer.android.widget.crouton.Crouton; + +@EActivity(resName = "activity_torrents") +@OptionsMenu(resName = "activity_torrents") +public class TorrentsActivity extends SherlockFragmentActivity implements OnNavigationListener, TorrentTasksExecutor, + NavigationFilterManager { // Navigation components @Bean @@ -85,15 +90,15 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi protected NavigationFilter currentFilter = null; @InstanceState protected boolean turleModeEnabled = false; - + // Torrents list components - @FragmentById(resName="torrent_list") + @FragmentById(resName = "torrent_list") protected TorrentsFragment fragmentTorrents; - + // Details view components - @FragmentById(resName="torrent_details") + @FragmentById(resName = "torrent_details") protected DetailsFragment fragmentDetails; - + @AfterViews protected void init() { @@ -119,24 +124,24 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi // Connect to the last used server ServerSetting lastUsed = applicationSettings.getLastUsedServer(); if (lastUsed == null) { - // No server settings yet; + // No server settings yet; return; } // Set this as selection in the action bar spinner; we can use the server setting key since we have stable ids // TODO: Does this call the action bar item selection callback? And refreshes? getSupportActionBar().setSelectedNavigationItem(lastUsed.getOrder()); - + // Handle any start up intents if (firstStart) { handleStartIntent(); } - + } - + @Override protected void onResume() { super.onResume(); - + // Refresh server settings navigationSpinnerAdapter.updateServers(applicationSettings.getServerSettings()); ServerSetting lastUsed = applicationSettings.getLastUsedServer(); @@ -166,11 +171,11 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi } return true; } - + @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); - + // No connection yet; hide all menu options except settings if (currentConnection == null) { menu.findItem(R.id.action_add).setVisible(false); @@ -187,7 +192,7 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); return true; } - + // There is a connection (read: settings to some server known) menu.findItem(R.id.action_add).setVisible(true); menu.findItem(R.id.action_search).setVisible(navigationHelper.enableSearchUi()); @@ -205,7 +210,7 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi return true; } - + /** * Called when an item in the action bar navigation spinner was selected */ @@ -220,7 +225,7 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi // A header was selected; no action return false; } - + // Handles clicks (selections) on the dedicated list of filter items (if it exists) // NOTE: Unfortunately we cannot use the @ItemSelect(R.id.filters_list) annotation as it throws NPE exceptions when // the list doesn't exist (read: on small screens) @@ -229,29 +234,30 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi public void onItemSelected(AdapterView parent, View view, int position, long id) { filterSelected((SimpleListItem) filtersList.getAdapter().getItem(position)); } + @Override public void onNothingSelected(AdapterView parent) { // TODO: Check if this happens } }; - + /** * A new filter was selected; update the view over the current data * @param selected True if the filter item was selected, false if it was deselected * @param item The touched filter item */ protected void filterSelected(SimpleListItem item) { - + // Server selection if (item instanceof ServerSetting) { ServerSetting server = (ServerSetting) item; - + if (currentConnection != null && server.equals(currentConnection.getSettings())) { // Already connected to this server; just ask for a refresh instead refreshTorrents(); return; } - + // Update connection to the newly selected server and refresh currentConnection = server.createServerAdapter(); applicationSettings.setLastUsedServer(server); @@ -261,8 +267,8 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi return; } - - // Status type or label selection - both of which are navigation filters + + // Status type or label selection - both of which are navigation filters if (item instanceof NavigationFilter) { currentFilter = (NavigationFilter) item; fragmentTorrents.applyFilter(currentFilter); @@ -271,7 +277,7 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi fragmentDetails.clear(); } } - + } @Override @@ -290,7 +296,7 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi */ private void updateFragmentVisibility(boolean hasServerSettings) { if (filtersList != null) - filtersList.setVisibility(hasServerSettings? View.VISIBLE: View.GONE); + filtersList.setVisibility(hasServerSettings ? View.VISIBLE : View.GONE); if (fragmentDetails != null) getSupportFragmentManager().beginTransaction().hide(fragmentDetails).commit(); } @@ -302,32 +308,32 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi // TODO: Handle start intent } - @OptionsItem(resName="action_refresh") + @OptionsItem(resName = "action_refresh") protected void refreshScreen() { refreshTorrents(); getAdditionalStats(); } - @OptionsItem(resName="action_enableturtle") + @OptionsItem(resName = "action_enableturtle") protected void enableTurtleMode() { updateTurtleMode(true); } - @OptionsItem(resName="action_disableturtle") + @OptionsItem(resName = "action_disableturtle") protected void disableTurtleMode() { updateTurtleMode(false); } - @OptionsItem(resName="action_settings") + @OptionsItem(resName = "action_settings") protected void openSettings() { MainSettingsActivity_.intent(this).start(); } - @OptionsItem(resName="action_help") + @OptionsItem(resName = "action_help") protected void openHelp() { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.transdroid.org/download/"))); } - + private void clearScreens() { // Clear the currently shown list of torrent and perhaps the details fragmentTorrents.clear(); @@ -342,9 +348,10 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi DaemonTaskResult result = RetrieveTask.create(currentConnection).execute(); fragmentTorrents.updateIsLoading(false); if (result instanceof RetrieveTaskSuccessResult) { - onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), ((RetrieveTaskSuccessResult) result).getLabels()); + onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), + ((RetrieveTaskSuccessResult) result).getLabels()); } else { - onCommunicationError((DaemonTaskFailureResult)result); + onCommunicationError((DaemonTaskFailureResult) result); } } @@ -354,10 +361,10 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi if (result instanceof GetStatsTaskSuccessResult) { onTurtleModeRetrieved(((GetStatsTaskSuccessResult) result).isAlternativeModeEnabled()); } else { - onCommunicationError((DaemonTaskFailureResult)result); + onCommunicationError((DaemonTaskFailureResult) result); } } - + @Background protected void updateTurtleMode(boolean enable) { DaemonTaskResult result = SetAlternativeModeTask.create(currentConnection, enable).execute(); @@ -365,7 +372,7 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi // Success; no need to retrieve it again - just update the visual indicator onTurtleModeRetrieved(enable); } else { - onCommunicationError((DaemonTaskFailureResult)result); + onCommunicationError((DaemonTaskFailureResult) result); } } @@ -465,16 +472,15 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi @UiThread protected void onTaskSucceeded(DaemonTaskSuccessResult result, int successMessageId, String... messageParams) { - // TODO: Properly report this success - Toast.makeText(this, getString(successMessageId, (Object[]) messageParams), Toast.LENGTH_LONG).show(); + Crouton.showText(this, getString(successMessageId, (Object[]) messageParams), + navigationHelper.CROUTON_INFO_STYLE); } @UiThread protected void onCommunicationError(DaemonTaskFailureResult result) { Log.i(this, result.getException().toString()); - // TODO: Properly report this error - Toast.makeText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())), - Toast.LENGTH_LONG).show(); + Crouton.showText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())), + navigationHelper.CROUTON_ERROR_STYLE); } @UiThread diff --git a/core/src/org/transdroid/core/gui/navigation/NavigationHelper.java b/core/src/org/transdroid/core/gui/navigation/NavigationHelper.java index 96d26edd..54025480 100644 --- a/core/src/org/transdroid/core/gui/navigation/NavigationHelper.java +++ b/core/src/org/transdroid/core/gui/navigation/NavigationHelper.java @@ -4,18 +4,31 @@ import org.androidannotations.annotations.EBean; import org.androidannotations.annotations.RootContext; import org.transdroid.core.R; +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; +import de.keyboardsurfer.android.widget.crouton.Style; +/** + * Helper for activities to make navigation-related decisions, such as when a device can display a larger, tablet style + * layout or how to display errors. + * @author Eric Kok + */ +@SuppressLint("ResourceAsColor") @EBean public class NavigationHelper { @RootContext protected Context context; + public Style CROUTON_ERROR_STYLE = new Style.Builder().setBackgroundColor(R.color.crouton_error).setTextSize(13) + .build(); + public Style CROUTON_INFO_STYLE = new Style.Builder().setBackgroundColor(R.color.crouton_info).setTextSize(13) + .build(); + /** * Whether any search-related UI components should be shown in the interface. At the moment returns false only if we * run as Transdroid Lite version. @@ -32,8 +45,8 @@ public class NavigationHelper { } /** - * Returns whether the device is considered small (i.e. a phone) rather than large (i.e. a tablet). Can, for example, - * be used to determine if a dialog should be shown full screen. Currently is true if the device's smallest + * Returns whether the device is considered small (i.e. a phone) rather than large (i.e. a tablet). Can, for + * example, be used to determine if a dialog should be shown full screen. Currently is true if the device's smallest * dimension is 500 dip. * @return True if the app runs on a small device, false otherwise */ @@ -58,19 +71,19 @@ public class NavigationHelper { public boolean enableRssUi() { return !context.getPackageName().equals("org.transdroid.lite"); } - + /** * Whether the navigation of server types and labels as filter are shown in a separate fragment. * @return True if navigation is in a separate fragment, false if the items are shown in the action bar spinner */ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) public boolean showFiltersInFragment() { - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { if (context.getResources().getConfiguration().screenWidthDp >= 600) { return true; } } return false; } - + } diff --git a/external/Crouton/.gitignore b/external/Crouton/.gitignore new file mode 100644 index 00000000..90705626 --- /dev/null +++ b/external/Crouton/.gitignore @@ -0,0 +1,41 @@ +# built application files +*.apk +*.ap_ +*.jar +gen-external-apklibs + +# keystore +*.keystore + +# files for the dex VM +*.dex + +# Java class files +*.class + +# generated files +bin/ +gen/ +target/ + +# Local configuration file (sdk path, etc) +local.properties + +# Eclipse project files +.classpath +.project +.metadata +.settings + +# IntelliJ files +.idea +*.iml + +# OSX files +.DS_Store + +#vi swap files +*.swp + +# maven target +target diff --git a/external/Crouton/LICENSE b/external/Crouton/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/external/Crouton/LICENSE @@ -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/external/Crouton/README.markdown b/external/Crouton/README.markdown new file mode 100644 index 00000000..8d4a3d77 --- /dev/null +++ b/external/Crouton/README.markdown @@ -0,0 +1,199 @@ +# Crouton +![Crouton](https://raw.github.com/keyboardsurfer/Crouton/master/sample/res/drawable-xhdpi/ic_launcher.png "Crouton logo") + +Context sensitive notifications for Android + +## Overview + +**Crouton** is a class that can be used by Android developers that feel the need for an **alternative to the Context insensitive [Toast](http://developer.android.com/reference/android/widget/Toast.html)**. + +A Crouton will be displayed at the position the developer decides. +Standard will be the of an application window. +You can line up multiple Croutons for display, that will be shown one after another. + +You can check some features in the Crouton Demo. + + + Crouton Demo on Google Play + + +If you're already using Crouton and just want to download the latest version of the library, follow [this link](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.keyboardsurfer.android.widget%22). + +### Changelog +#### Current version: 1.7 + +####[1.7](https://github.com/keyboardsurfer/Crouton/tree/1.7) + +- `Crouton.setOnClickListener(OnClickListener)` has been introduced. +- Infinite display of Crouton is possible via `Style.setDuration(Style.DURATION_INFINITE)` +- Via `Crouton.hide(Crouton)` a Crouton can be hidden. + +####[1.6](https://github.com/keyboardsurfer/Crouton/tree/1.6) + +- Crouton now can be used on any Android device with **API level 4+**. +- Changes the package name to `de.keyboardsurfer.android.widget` +- Adds possibility to set a custom width +- Can now be added to any ViewGroup (@coreform) +- Integration with TalkBack (@coreform) +- Adds Accessibility features (@coreform) +- Fixes bug that got Crouton out of sync with reality (@coreform) +- New [LifecycleCallback](https://github.com/keyboardsurfer/Crouton/blob/master/library/src/de/keyboardsurfer/android/widget/crouton/LifecycleCallback.java) (@coreform) +- initializeCroutonView was refactored, to make it easier on the eyes +- removes redundant initialization within Style.Builder +- documentation improvments + +#### older versions + +Please see the `git log` + +## Usage + +The API is kept as simple as the Toast API: + +Create a Crouton for any CharSequence: + + Crouton.makeText(Activity, CharSequence, [Style]).show(); + +Create a Crouton with a String from your application's resources: + + Crouton.makeText(Activity, int, Style).show(); + +Further you can attach a Crouton to any view like this: + + Crouton.makeText(Activity, int, Style, int).show(); + +If you would like a more graphical introduction to Crouton check out [this presentation](https://speakerdeck.com/keyboardsurfer/crouton-devfest-berlin-2012). + +##Important! + +In your Activity.onDestroy() make sure to call + + Crouton.cancelAllCroutons(); + +to cancel cancel all scheduled Croutons. + +This is a workaround and further description is available in #24. + +## Basic Examples +Currently you can use the three different Style attributes displayed below out of the box: + +![Alert](https://github.com/keyboardsurfer/Crouton/raw/master/res/Alert.png "Example of Style.ALERT") + +![Confirm](https://github.com/keyboardsurfer/Crouton/raw/master/res/Confirm.png "Example of Style.CONFIRM") + +![Info](https://github.com/keyboardsurfer/Crouton/raw/master/res/Info.png "Example of Style.INFO") + +## Extension and Modification + +The whole design of a Crouton is defined by [Style](https://github.com/keyboardsurfer/Crouton/blob/master/library/src/de/keyboardsurfer/android/widget/crouton/Style.java). + +You can use one of the styles Crouton ships with: **Style.ALERT**, **Style.CONFIRM** and **Style.INFO**. Or you can create your own Style. + +In general you can modify + +- display duration +- dimension settings +- options for the text to display +- custom Views +- appearance & disappearance Animation +- displayed Image + +Since [Style](https://github.com/keyboardsurfer/Crouton/blob/master/library/src/de/keyboardsurfer/android/widget/crouton/Style.java) is the general entry point for tweaking Croutons, go and see for yourself what can be done with it. + + +## Maven + +### From maven central + +Crouton is available in the maven central repository. + +To use crouton simply add + +```xml + + crouton + 1.7 + de.keyboardsurfer.android.widget + +``` + +to your pom.xml + +If you also want the sources or javadoc add the respective classifier + +```xml + sources +``` + +or + +```xml + javadoc +``` +to the dependency. + +If you are referencing a newer version of the Android Support Library in your application, you might want to exclude Crouton's dependency like this: + +```xml + + crouton + ${crouton.version} + de.keyboardsurfer.android.widget + + + com.google.android + support-v4 + + + +``` + +### DIY + +The build requires Maven. Operations are very simple: + +* `mvn -f library/pom.xml clean package` will build a `jar` library; +* `mvn clean package` will build a `jar` library and the sample application `apk`; +* `mvn -f library/pom.xml clean install` will put Crouton in your local Maven repository. + +After putting Crouton in the repository you can add it as a dependency. + +```xml + + crouton + 1.6 + de.keyboardsurfer.android.widget + +``` + +## Contribution + +### Pull requests welcome + +Feel free to contribute to Crouton. + +Either you found a bug or have created a new and awesome feature, just create a pull request. + +If you want to start to create a new feature or have any other questions regarding Crouton, [file an issue](https://github.com/keyboardsurfer/Crouton/issues/new). +I'll try to answer as soon as I find the time. + +### Formatting + +For contributors using Eclipse there's a formatter available at the [download section](https://github.com/downloads/keyboardsurfer/Crouton/Crouton_Eclipseformatter.xml). + +In order to reduce merging pains on my end, please use this formatter or format your commit in a way similar to it's example. + +If you're using IDEA, the Eclipse Formatter plugin should allow you to use the formatter as well. + +## License + +* [Apache Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) + +## Attributions + +The initial version was written by Benjamin Weiss at [Neofonie Mobile GmbH](http://mobile.neofonie.de). + +The name and the idea of [Crouton](https://github.com/keyboardsurfer/Crouton/blob/master/library/src/de/keyboardsurfer/android/widget/crouton/Crouton.java) originates in a [blog article](http://android.cyrilmottier.com/?p=773) by Cyril Mottier. + +The Crouton logo has been created by [Marie Schweiz](http://marie-schweiz.de). diff --git a/external/Crouton/build.gradle b/external/Crouton/build.gradle new file mode 100644 index 00000000..e69de29b diff --git a/external/Crouton/library/.classpath b/external/Crouton/library/.classpath new file mode 100644 index 00000000..a4763d1e --- /dev/null +++ b/external/Crouton/library/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/external/Crouton/library/.project b/external/Crouton/library/.project new file mode 100644 index 00000000..a5d8dde2 --- /dev/null +++ b/external/Crouton/library/.project @@ -0,0 +1,33 @@ + + + Crouton + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/external/Crouton/library/AndroidManifest.xml b/external/Crouton/library/AndroidManifest.xml new file mode 100644 index 00000000..5f62e486 --- /dev/null +++ b/external/Crouton/library/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/external/Crouton/library/build.xml b/external/Crouton/library/build.xml new file mode 100644 index 00000000..2f6f323a --- /dev/null +++ b/external/Crouton/library/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/Crouton/library/pom.xml b/external/Crouton/library/pom.xml new file mode 100644 index 00000000..864bcce0 --- /dev/null +++ b/external/Crouton/library/pom.xml @@ -0,0 +1,112 @@ + + + + + + 4.0.0 + + Crouton + Context sensitive notifications for Android + https://github.com/keyboardsurfer/Crouton + crouton + de.keyboardsurfer.android.widget + 1.7 + jar + + + UTF-8 + 4.1.1.4 + 16 + + + + + keyboardsurfer + Benjamin Weiss + + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + git@github.com:keyboardsurfer/Crouton.git + scm:git:git@github.com:keyboardsurfer/Crouton.git + scm:git:git@github.com:keyboardsurfer/Crouton.git + + + + + android + ${android.version} + com.google.android + provided + + + com.google.android + support-v4 + r11 + + + + + src + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9 + + + attach-javadocs + + jar + + + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + 3.5.0 + + + + + diff --git a/external/Crouton/library/project.properties b/external/Crouton/library/project.properties new file mode 100644 index 00000000..0b79b87b --- /dev/null +++ b/external/Crouton/library/project.properties @@ -0,0 +1,17 @@ +# 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 edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. + +target=android-16 +android.library=true +android.library.reference.1=../../JakeWharton-ActionBarSherlock/library diff --git a/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/Crouton.java b/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/Crouton.java new file mode 100644 index 00000000..4d9d95b8 --- /dev/null +++ b/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/Crouton.java @@ -0,0 +1,825 @@ +/* + * Copyright 2012 - 2013 Benjamin Weiss + * Copyright 2012 Neofonie Mobile GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 de.keyboardsurfer.android.widget.crouton; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Shader; +import android.graphics.Typeface; +import android.graphics.drawable.BitmapDrawable; +import android.util.TypedValue; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +/* + * Based on an article by Cyril Mottier (http://android.cyrilmottier.com/?p=773)
+ */ + + +/** + * Displays information in a non-invasive context related manner. Like + * {@link android.widget.Toast}, but better. + *

+ * Important: + * Call {@link Crouton#clearCroutonsForActivity(Activity)} within + * {@link android.app.Activity#onDestroy()} to avoid {@link Context} leaks. + */ +public final class Crouton { + private static final int IMAGE_ID = 0x100; + private static final int TEXT_ID = 0x101; + private final CharSequence text; + private final Style style; + private final View customView; + + private OnClickListener onClickListener; + + private Activity activity; + private ViewGroup viewGroup; + private FrameLayout croutonView; + private Animation inAnimation; + private Animation outAnimation; + private LifecycleCallback lifecycleCallback = null; + + /** + * Creates the {@link Crouton}. + * + * @param activity + * The {@link Activity} that the {@link Crouton} should be attached + * to. + * @param text + * The text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + */ + private Crouton(Activity activity, CharSequence text, Style style) { + if ((activity == null) || (text == null) || (style == null)) { + throw new IllegalArgumentException("Null parameters are not accepted"); + } + + this.activity = activity; + this.viewGroup = null; + this.text = text; + this.style = style; + this.customView = null; + } + + /** + * Creates the {@link Crouton}. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param text + * The text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + * @param viewGroup + * The {@link ViewGroup} that this {@link Crouton} should be added to. + */ + private Crouton(Activity activity, CharSequence text, Style style, ViewGroup viewGroup) { + if ((activity == null) || (text == null) || (style == null)) { + throw new IllegalArgumentException("Null parameters are not accepted"); + } + + this.activity = activity; + this.text = text; + this.style = style; + this.viewGroup = viewGroup; + this.customView = null; + } + + /** + * Creates the {@link Crouton}. + * + * @param activity + * The {@link Activity} that the {@link Crouton} should be attached + * to. + * @param customView + * The custom {@link View} to display + */ + private Crouton(Activity activity, View customView) { + if ((activity == null) || (customView == null)) { + throw new IllegalArgumentException("Null parameters are not accepted"); + } + + this.activity = activity; + this.viewGroup = null; + this.customView = customView; + this.style = new Style.Builder().build(); + this.text = null; + } + + /** + * Creates the {@link Crouton}. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param customView + * The custom {@link View} to display + * @param viewGroup + * The {@link ViewGroup} that this {@link Crouton} should be added to. + */ + private Crouton(Activity activity, View customView, ViewGroup viewGroup) { + if ((activity == null) || (customView == null)) { + throw new IllegalArgumentException("Null parameters are not accepted"); + } + + this.activity = activity; + this.customView = customView; + this.viewGroup = viewGroup; + this.style = new Style.Builder().build(); + this.text = null; + } + + /** + * Creates a {@link Crouton} with provided text and style for a given + * activity. + * + * @param activity + * The {@link Activity} that the {@link Crouton} should be attached + * to. + * @param text + * The text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + * + * @return The created {@link Crouton}. + */ + public static Crouton makeText(Activity activity, CharSequence text, Style style) { + return new Crouton(activity, text, style); + } + + /** + * Creates a {@link Crouton} with provided text and style for a given + * activity. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param text + * The text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + * @param viewGroup + * The {@link ViewGroup} that this {@link Crouton} should be added to. + * + * @return The created {@link Crouton}. + */ + public static Crouton makeText(Activity activity, CharSequence text, Style style, ViewGroup viewGroup) { + return new Crouton(activity, text, style, viewGroup); + } + + /** + * Creates a {@link Crouton} with provided text and style for a given + * activity. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param text + * The text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + * @param viewGroupResId + * The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to. + * + * @return The created {@link Crouton}. + */ + public static Crouton makeText(Activity activity, CharSequence text, Style style, int viewGroupResId) { + return new Crouton(activity, text, style, (ViewGroup) activity.findViewById(viewGroupResId)); + } + + + /** + * Creates a {@link Crouton} with provided text-resource and style for a given + * activity. + * + * @param activity + * The {@link Activity} that the {@link Crouton} should be attached + * to. + * @param textResourceId + * The resource id of the text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + * + * @return The created {@link Crouton}. + */ + public static Crouton makeText(Activity activity, int textResourceId, Style style) { + return makeText(activity, activity.getString(textResourceId), style); + } + + /** + * Creates a {@link Crouton} with provided text-resource and style for a given + * activity. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param textResourceId + * The resource id of the text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + * @param viewGroup + * The {@link ViewGroup} that this {@link Crouton} should be added to. + * + * @return The created {@link Crouton}. + */ + public static Crouton makeText(Activity activity, int textResourceId, Style style, ViewGroup viewGroup) { + return makeText(activity, activity.getString(textResourceId), style, viewGroup); + } + + /** + * Creates a {@link Crouton} with provided text-resource and style for a given + * activity. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param textResourceId + * The resource id of the text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + * @param viewGroupResId + * The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to. + * + * @return The created {@link Crouton}. + */ + public static Crouton makeText(Activity activity, int textResourceId, Style style, int viewGroupResId) { + return makeText(activity, activity.getString(textResourceId), style, + (ViewGroup) activity.findViewById(viewGroupResId)); + } + + + /** + * Creates a {@link Crouton} with provided text-resource and style for a given + * activity. + * + * @param activity + * The {@link Activity} that the {@link Crouton} should be attached + * to. + * @param customView + * The custom {@link View} to display + * + * @return The created {@link Crouton}. + */ + public static Crouton make(Activity activity, View customView) { + return new Crouton(activity, customView); + } + + /** + * Creates a {@link Crouton} with provided text-resource and style for a given + * activity. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param customView + * The custom {@link View} to display + * @param viewGroup + * The {@link ViewGroup} that this {@link Crouton} should be added to. + * + * @return The created {@link Crouton}. + */ + public static Crouton make(Activity activity, View customView, ViewGroup viewGroup) { + return new Crouton(activity, customView, viewGroup); + } + + /** + * Creates a {@link Crouton} with provided text-resource and style for a given + * activity. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param customView + * The custom {@link View} to display + * @param viewGroupResId + * The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to. + * + * @return The created {@link Crouton}. + */ + public static Crouton make(Activity activity, View customView, int viewGroupResId) { + return new Crouton(activity, customView, (ViewGroup) activity.findViewById(viewGroupResId)); + } + + /** + * Creates a {@link Crouton} with provided text and style for a given activity + * and displays it directly. + * + * @param activity + * The {@link android.app.Activity} that the {@link Crouton} should + * be attached to. + * @param text + * The text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + */ + public static void showText(Activity activity, CharSequence text, Style style) { + makeText(activity, text, style).show(); + } + + /** + * Creates a {@link Crouton} with provided text and style for a given activity + * and displays it directly. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param text + * The text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + * @param viewGroup + * The {@link ViewGroup} that this {@link Crouton} should be added to. + */ + public static void showText(Activity activity, CharSequence text, Style style, ViewGroup viewGroup) { + makeText(activity, text, style, viewGroup).show(); + } + + /** + * Creates a {@link Crouton} with provided text and style for a given activity + * and displays it directly. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param text + * The text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + * @param viewGroupResId + * The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to. + */ + public static void showText(Activity activity, CharSequence text, Style style, int viewGroupResId) { + makeText(activity, text, style, (ViewGroup) activity.findViewById(viewGroupResId)).show(); + } + + + /** + * Creates a {@link Crouton} with provided text and style for a given activity + * and displays it directly. + * + * @param activity + * The {@link android.app.Activity} that the {@link Crouton} should + * be attached to. + * @param customView + * The custom {@link View} to display + */ + public static void show(Activity activity, View customView) { + make(activity, customView).show(); + } + + /** + * Creates a {@link Crouton} with provided text and style for a given activity + * and displays it directly. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param customView + * The custom {@link View} to display + * @param viewGroup + * The {@link ViewGroup} that this {@link Crouton} should be added to. + */ + public static void show(Activity activity, View customView, ViewGroup viewGroup) { + make(activity, customView, viewGroup).show(); + } + + /** + * Creates a {@link Crouton} with provided text and style for a given activity + * and displays it directly. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param customView + * The custom {@link View} to display + * @param viewGroupResId + * The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to. + */ + public static void show(Activity activity, View customView, int viewGroupResId) { + make(activity, customView, viewGroupResId).show(); + } + + /** + * Creates a {@link Crouton} with provided text-resource and style for a given + * activity and displays it directly. + * + * @param activity + * The {@link Activity} that the {@link Crouton} should be attached + * to. + * @param textResourceId + * The resource id of the text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + */ + public static void showText(Activity activity, int textResourceId, Style style) { + showText(activity, activity.getString(textResourceId), style); + } + + /** + * Creates a {@link Crouton} with provided text-resource and style for a given + * activity and displays it directly. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param textResourceId + * The resource id of the text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + * @param viewGroup + * The {@link ViewGroup} that this {@link Crouton} should be added to. + */ + public static void showText(Activity activity, int textResourceId, Style style, ViewGroup viewGroup) { + showText(activity, activity.getString(textResourceId), style, viewGroup); + } + + /** + * Creates a {@link Crouton} with provided text-resource and style for a given + * activity and displays it directly. + * + * @param activity + * The {@link Activity} that represents the context in which the Crouton should exist. + * @param textResourceId + * The resource id of the text you want to display. + * @param style + * The style that this {@link Crouton} should be created with. + * @param viewGroupResId + * The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to. + */ + public static void showText(Activity activity, int textResourceId, Style style, int viewGroupResId) { + showText(activity, activity.getString(textResourceId), style, viewGroupResId); + } + + /** + * Allows hiding of a previously displayed {@link Crouton}. + * @param crouton The {@link Crouton} you want to hide. + */ + public static void hide(Crouton crouton) { + Manager.getInstance().removeCrouton(crouton); + } + + /** + * Cancels all queued {@link Crouton}s. If there is a {@link Crouton} + * displayed currently, it will be the last one displayed. + */ + public static void cancelAllCroutons() { + Manager.getInstance().clearCroutonQueue(); + } + + /** + * Clears (and removes from {@link Activity}'s content view, if necessary) all + * croutons for the provided activity + * + * @param activity + * - The {@link Activity} to clear the croutons for. + */ + public static void clearCroutonsForActivity(Activity activity) { + Manager.getInstance().clearCroutonsForActivity(activity); + } + + /** + * Cancels a {@link Crouton} immediately. + */ + public void cancel() { + Manager manager = Manager.getInstance(); + manager.removeCroutonImmediately(this); + } + + /** + * Displays the {@link Crouton}. If there's another {@link Crouton} visible at + * the time, this {@link Crouton} will be displayed afterwards. + */ + public void show() { + Manager.getInstance().add(this); + } + + public Animation getInAnimation() { + if ((null == this.inAnimation) && (null != this.activity)) { + if (getStyle().inAnimationResId > 0) { + this.inAnimation = AnimationUtils.loadAnimation(getActivity(), getStyle().inAnimationResId); + } else { + this.inAnimation = DefaultAnimationsBuilder.buildDefaultSlideInDownAnimation(); + } + } + + return inAnimation; + } + + public Animation getOutAnimation() { + if ((null == this.outAnimation) && (null != this.activity)) { + if (getStyle().outAnimationResId > 0) { + this.outAnimation = AnimationUtils.loadAnimation(getActivity(), getStyle().outAnimationResId); + } else { + this.outAnimation = DefaultAnimationsBuilder.buildDefaultSlideOutUpAnimation(); + } + } + + return outAnimation; + } + + /** + * @param lifecycleCallback + * Callback object for notable events in the life of a Crouton. + */ + public void setLifecycleCallback(LifecycleCallback lifecycleCallback) { + this.lifecycleCallback = lifecycleCallback; + } + + /** + * Convenience method to get the license text for embedding within your application. + * @return + * The license text. + */ + public String getLicenseText() { + return "This application uses the Crouton library.\n\n" + + "Copyright 2012 - 2013 Benjamin Weiss \n" + + "Copyright 2012 Neofonie Mobile GmbH\n" + + "\n" + + "Licensed under the Apache License, Version 2.0 (the \"License\");\n" + + "you may not use this file except in compliance with the License.\n" + + "You may obtain a copy of the License at\n" + + "\n" + + " http://www.apache.org/licenses/LICENSE-2.0\n" + + "\n" + + "Unless required by applicable law or agreed to in writing, software\n" + + "distributed under the License is distributed on an \"AS IS\" BASIS,\n" + + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + + "See the License for the specific language governing permissions and\n" + + "limitations under the License."; + } + + /** + * Allows setting of an {@link OnClickListener} directly to a {@link Crouton} without having to use a custom view. + * @param onClickListener The {@link OnClickListener} to set. + * @return this {@link Crouton}. + */ + public Crouton setOnClickListener(OnClickListener onClickListener){ + this.onClickListener = onClickListener; + return this; + } + + /** + * @return true if the {@link Crouton} is being displayed, else + * false. + */ + boolean isShowing() { + return (null != activity) && (null != croutonView) && (null != croutonView.getParent()); + } + + /** + * Removes the activity reference this {@link Crouton} is holding + */ + void detachActivity() { + activity = null; + } + + /** + * Removes the viewGroup reference this {@link Crouton} is holding + */ + void detachViewGroup() { + viewGroup = null; + } + + /** + * Removes the lifecycleCallback reference this {@link Crouton} is holding + */ + void detachLifecycleCallback() { + lifecycleCallback = null; + } + + /** + * @return the lifecycleCallback + */ + LifecycleCallback getLifecycleCallback() { + return lifecycleCallback; + } + + /** + * @return the style + */ + Style getStyle() { + return style; + } + + /** + * @return the activity + */ + Activity getActivity() { + return activity; + } + + /** + * @return the viewGroup + */ + ViewGroup getViewGroup() { + return viewGroup; + } + + /** + * @return the text + */ + CharSequence getText() { + return text; + } + + /** + * @return the view + */ + View getView() { + // return the custom view if one exists + if (null != this.customView) { + return this.customView; + } + + // if already setup return the view + if (null == this.croutonView) { + initializeCroutonView(); + } + + return croutonView; + } + + private void initializeCroutonView() { + Resources resources = this.activity.getResources(); + + this.croutonView = initializeCroutonViewGroup(resources); + + // create content view + RelativeLayout contentView = initializeContentView(resources); + this.croutonView.addView(contentView); + } + + private FrameLayout initializeCroutonViewGroup(Resources resources) { + FrameLayout croutonView = new FrameLayout(this.activity); + + if(null != onClickListener) + croutonView.setOnClickListener(onClickListener); + + final int height; + if (this.style.heightDimensionResId > 0) { + height = resources.getDimensionPixelSize(this.style.heightDimensionResId); + } else { + height = this.style.heightInPixels; + } + + final int width; + if (this.style.widthDimensionResId > 0) { + width = resources.getDimensionPixelSize(this.style.widthDimensionResId); + } else { + width = this.style.widthInPixels; + } + + croutonView.setLayoutParams( + new FrameLayout.LayoutParams(width != 0 ? width : FrameLayout.LayoutParams.MATCH_PARENT, height)); + + // set background + if (this.style.backgroundColorValue != -1) { + croutonView.setBackgroundColor(this.style.backgroundColorValue); + } else { + croutonView.setBackgroundColor(resources.getColor(this.style.backgroundColorResourceId)); + } + + // set the background drawable if set. This will override the background + // color. + if (this.style.backgroundDrawableResourceId != 0) { + Bitmap background = BitmapFactory.decodeResource(resources, this.style.backgroundDrawableResourceId); + BitmapDrawable drawable = new BitmapDrawable(resources, background); + if (this.style.isTileEnabled) { + drawable.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); + } + croutonView.setBackgroundDrawable(drawable); + } + return croutonView; + } + + private RelativeLayout initializeContentView(final Resources resources) { + RelativeLayout contentView = new RelativeLayout(this.activity); + contentView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, + RelativeLayout.LayoutParams.WRAP_CONTENT)); + + // set padding + int padding = this.style.paddingInPixels; + + // if a padding dimension has been set, this will overwrite any padding + // in pixels + if (this.style.paddingDimensionResId > 0) { + padding = resources.getDimensionPixelSize(this.style.paddingDimensionResId); + } + contentView.setPadding(padding, padding, padding, padding); + + // only setup image if one is requested + ImageView image = null; + if ((null != this.style.imageDrawable) || (0 != this.style.imageResId)) { + image = initializeImageView(); + contentView.addView(image, image.getLayoutParams()); + } + + TextView text = initializeTextView(resources); + + RelativeLayout.LayoutParams textParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, + RelativeLayout.LayoutParams.WRAP_CONTENT); + if (null != image) { + textParams.addRule(RelativeLayout.RIGHT_OF, image.getId()); + } + contentView.addView(text, textParams); + return contentView; + } + + private TextView initializeTextView(final Resources resources) { + TextView text = new TextView(this.activity); + text.setId(TEXT_ID); + text.setText(this.text); + text.setTypeface(Typeface.DEFAULT_BOLD); + text.setGravity(this.style.gravity); + + // set the text color if set + if (this.style.textColorResourceId != 0) { + text.setTextColor(resources.getColor(this.style.textColorResourceId)); + } + + // Set the text size. If the user has set a text size and text + // appearance, the text size in the text appearance + // will override this. + if (this.style.textSize != 0) { + text.setTextSize(TypedValue.COMPLEX_UNIT_SP, this.style.textSize); + } + + // Setup the shadow if requested + if (this.style.textShadowColorResId != 0) { + initializeTextViewShadow(resources, text); + } + + // Set the text appearance + if (this.style.textAppearanceResId != 0) { + text.setTextAppearance(this.activity, this.style.textAppearanceResId); + } + return text; + } + + private void initializeTextViewShadow(final Resources resources, final TextView text) { + int textShadowColor = resources.getColor(this.style.textShadowColorResId); + float textShadowRadius = this.style.textShadowRadius; + float textShadowDx = this.style.textShadowDx; + float textShadowDy = this.style.textShadowDy; + text.setShadowLayer(textShadowRadius, textShadowDx, textShadowDy, textShadowColor); + } + + private ImageView initializeImageView() { + ImageView image; + image = new ImageView(this.activity); + image.setId(IMAGE_ID); + image.setAdjustViewBounds(true); + image.setScaleType(this.style.imageScaleType); + + // set the image drawable if not null + if (null != this.style.imageDrawable) { + image.setImageDrawable(this.style.imageDrawable); + } + + // set the image resource if not 0. This will overwrite the drawable + // if both are set + if (this.style.imageResId != 0) { + image.setImageResource(this.style.imageResId); + } + + RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.WRAP_CONTENT, + RelativeLayout.LayoutParams.WRAP_CONTENT); + imageParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE); + imageParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE); + + image.setLayoutParams(imageParams); + + return image; + } + + @Override + public String toString() { + return "Crouton{" + + "text=" + text + + ", style=" + style + + ", customView=" + customView + + ", activity=" + activity + + ", viewGroup=" + viewGroup + + ", croutonView=" + croutonView + + ", inAnimation=" + inAnimation + + ", outAnimation=" + outAnimation + + ", lifecycleCallback=" + lifecycleCallback + + '}'; + } +} diff --git a/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/DefaultAnimationsBuilder.java b/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/DefaultAnimationsBuilder.java new file mode 100644 index 00000000..b1109c86 --- /dev/null +++ b/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/DefaultAnimationsBuilder.java @@ -0,0 +1,85 @@ +/* + * Copyright 2012 - 2013 Benjamin Weiss + * Copyright 2012 Neofonie Mobile GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 de.keyboardsurfer.android.widget.crouton; + +import android.view.animation.Animation; +import android.view.animation.TranslateAnimation; + +/** + * Builds the default animations for showing and hiding a {@link Crouton}. + */ +final class DefaultAnimationsBuilder { + private static Animation slideInDownAnimation, slideOutUpAnimation; + + protected static final class SlideInDownAnimationParameters { + private SlideInDownAnimationParameters() { + /* no-op */ + } + + public static final float FROM_X_DELTA = 0; + public static final float TO_X_DELTA = 0; + public static final float FROM_Y_DELTA = -50; + public static final float TO_Y_DELTA = 0; + + public static final long DURATION = 400; + } + + protected static final class SlideOutUpAnimationParameters { + private SlideOutUpAnimationParameters() { + /* no-op */ + } + + public static final float FROM_X_DELTA = 0; + public static final float TO_X_DELTA = 0; + public static final float FROM_Y_DELTA = 0; + public static final float TO_Y_DELTA = -50; + + public static final long DURATION = 400; + } + + private DefaultAnimationsBuilder() { + /* no-op */ + } + + /** + * @return The default Animation for a showing {@link Crouton}. + */ + public static Animation buildDefaultSlideInDownAnimation() { + if (null == slideInDownAnimation) { + slideInDownAnimation = new TranslateAnimation(SlideInDownAnimationParameters.FROM_X_DELTA, + SlideInDownAnimationParameters.TO_X_DELTA, + SlideInDownAnimationParameters.FROM_Y_DELTA, SlideInDownAnimationParameters.TO_Y_DELTA); + slideInDownAnimation.setDuration(SlideInDownAnimationParameters.DURATION); + } + + return slideInDownAnimation; + } + + /** + * @return The default Animation for a hiding {@link Crouton}. + */ + public static Animation buildDefaultSlideOutUpAnimation() { + if (null == slideOutUpAnimation) { + slideOutUpAnimation = new TranslateAnimation(SlideOutUpAnimationParameters.FROM_X_DELTA, + SlideOutUpAnimationParameters.TO_X_DELTA, + SlideOutUpAnimationParameters.FROM_Y_DELTA, SlideOutUpAnimationParameters.TO_Y_DELTA); + slideOutUpAnimation.setDuration(SlideOutUpAnimationParameters.DURATION); + } + return slideOutUpAnimation; + } +} diff --git a/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/LifecycleCallback.java b/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/LifecycleCallback.java new file mode 100644 index 00000000..a2ade574 --- /dev/null +++ b/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/LifecycleCallback.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012 - 2013 Benjamin Weiss + * Copyright 2012 Neofonie Mobile GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 de.keyboardsurfer.android.widget.crouton; + +public interface LifecycleCallback { + public void onDisplayed(); + public void onRemoved(); + //public void onCeasarDressing(); +} diff --git a/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/Manager.java b/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/Manager.java new file mode 100644 index 00000000..0d462941 --- /dev/null +++ b/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/Manager.java @@ -0,0 +1,403 @@ +/* + * Copyright 2012 - 2013 Benjamin Weiss + * Copyright 2012 Neofonie Mobile GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 de.keyboardsurfer.android.widget.crouton; + +import android.app.Activity; +import android.content.Context; +import android.os.Build; +import android.os.Handler; +import android.os.Message; +import android.support.v4.view.accessibility.AccessibilityEventCompat; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.widget.FrameLayout; +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; + + +/** + * Manages the lifecycle of {@link Crouton}s. + */ +final class Manager extends Handler { + private static final class Messages { + private Messages() { /* no-op */ + } + + public static final int DISPLAY_CROUTON = 0xc2007; + public static final int ADD_CROUTON_TO_VIEW = 0xc20074dd; + public static final int REMOVE_CROUTON = 0xc2007de1; + } + + private static Manager INSTANCE; + + private Queue croutonQueue; + + private Manager() { + croutonQueue = new LinkedBlockingQueue(); + } + + /** + * @return The currently used instance of the {@link Manager}. + */ + static synchronized Manager getInstance() { + if (null == INSTANCE) { + INSTANCE = new Manager(); + } + + return INSTANCE; + } + + /** + * Inserts a {@link Crouton} to be displayed. + * + * @param crouton + * The {@link Crouton} to be displayed. + */ + void add(Crouton crouton) { + croutonQueue.add(crouton); + displayCrouton(); + } + + /** + * Displays the next {@link Crouton} within the queue. + */ + private void displayCrouton() { + if (croutonQueue.isEmpty()) { + return; + } + + // First peek whether the Crouton has an activity. + final Crouton currentCrouton = croutonQueue.peek(); + + // If the activity is null we poll the Crouton off the queue. + if (null == currentCrouton.getActivity()) { + croutonQueue.poll(); + } + + if (!currentCrouton.isShowing()) { + // Display the Crouton + sendMessage(currentCrouton, Messages.ADD_CROUTON_TO_VIEW); + if (null != currentCrouton.getLifecycleCallback()) { + currentCrouton.getLifecycleCallback().onDisplayed(); + } + } else { + sendMessageDelayed(currentCrouton, Messages.DISPLAY_CROUTON, calculateCroutonDuration(currentCrouton)); + } + } + + private long calculateCroutonDuration(Crouton crouton) { + long croutonDuration = crouton.getStyle().durationInMilliseconds; + croutonDuration += crouton.getInAnimation().getDuration(); + croutonDuration += crouton.getOutAnimation().getDuration(); + return croutonDuration; + } + + /** + * Sends a {@link Crouton} within a {@link Message}. + * + * @param crouton + * The {@link Crouton} that should be sent. + * @param messageId + * The {@link Message} id. + */ + private void sendMessage(Crouton crouton, final int messageId) { + final Message message = obtainMessage(messageId); + message.obj = crouton; + sendMessage(message); + } + + /** + * Sends a {@link Crouton} within a delayed {@link Message}. + * + * @param crouton + * The {@link Crouton} that should be sent. + * @param messageId + * The {@link Message} id. + * @param delay + * The delay in milliseconds. + */ + private void sendMessageDelayed(Crouton crouton, final int messageId, final long delay) { + Message message = obtainMessage(messageId); + message.obj = crouton; + sendMessageDelayed(message, delay); + } + + /* + * (non-Javadoc) + * + * @see android.os.Handler#handleMessage(android.os.Message) + */ + @Override + public void handleMessage(Message message) { + final Crouton crouton = (Crouton) message.obj; + + switch (message.what) { + case Messages.DISPLAY_CROUTON: { + displayCrouton(); + break; + } + + case Messages.ADD_CROUTON_TO_VIEW: { + addCroutonToView(crouton); + break; + } + + case Messages.REMOVE_CROUTON: { + removeCrouton(crouton); + if (null != crouton.getLifecycleCallback()) { + crouton.getLifecycleCallback().onRemoved(); + } + break; + } + + default: { + super.handleMessage(message); + break; + } + } + } + + /** + * Adds a {@link Crouton} to the {@link ViewParent} of it's {@link Activity}. + * + * @param crouton + * The {@link Crouton} that should be added. + */ + private void addCroutonToView(Crouton crouton) { + // don't add if it is already showing + if (crouton.isShowing()) { + return; + } + + View croutonView = crouton.getView(); + if (null == croutonView.getParent()) { + ViewGroup.LayoutParams params = croutonView.getLayoutParams(); + if (null == params) { + params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + // display Crouton in ViewGroup is it has been supplied + if (null != crouton.getViewGroup()) { + // TODO implement add to last position feature (need to align with how this will be requested for activity) + if (crouton.getViewGroup() instanceof FrameLayout) { + crouton.getViewGroup().addView(croutonView, params); + } else { + crouton.getViewGroup().addView(croutonView, 0, params); + } + } else { + Activity activity = crouton.getActivity(); + if (null == activity || activity.isFinishing()) { + return; + } + activity.addContentView(croutonView, params); + } + } + croutonView.startAnimation(crouton.getInAnimation()); + announceForAccessibilityCompat(crouton.getActivity(), crouton.getText()); + if (Style.DURATION_INFINITE != crouton.getStyle().durationInMilliseconds) { + sendMessageDelayed(crouton, Messages.REMOVE_CROUTON, + crouton.getStyle().durationInMilliseconds + crouton.getInAnimation().getDuration()); + } + } + + /** + * Removes the {@link Crouton}'s view after it's display + * durationInMilliseconds. + * + * @param crouton + * The {@link Crouton} added to a {@link ViewGroup} and should be + * removed. + */ + protected void removeCrouton(Crouton crouton) { + View croutonView = crouton.getView(); + ViewGroup croutonParentView = (ViewGroup) croutonView.getParent(); + + if (null != croutonParentView) { + croutonView.startAnimation(crouton.getOutAnimation()); + + // Remove the Crouton from the queue. + Crouton removed = croutonQueue.poll(); + + // Remove the crouton from the view's parent. + croutonParentView.removeView(croutonView); + if (null != removed) { + removed.detachActivity(); + removed.detachViewGroup(); + if (null != removed.getLifecycleCallback()) { + removed.getLifecycleCallback().onRemoved(); + } + removed.detachLifecycleCallback(); + } + + // Send a message to display the next crouton but delay it by the out + // animation duration to make sure it finishes + sendMessageDelayed(crouton, Messages.DISPLAY_CROUTON, crouton.getOutAnimation().getDuration()); + } + } + + /** + * Removes a {@link Crouton} immediately, even when it's currently being + * displayed. + * + * @param crouton + * The {@link Crouton} that should be removed. + */ + void removeCroutonImmediately(Crouton crouton) { + // if Crouton has already been displayed then it may not be in the queue (because it was popped). + // This ensures the displayed Crouton is removed from its parent immediately, whether another instance + // of it exists in the queue or not. + // Note: crouton.isShowing() is false here even if it really is showing, as croutonView object in + // Crouton seems to be out of sync with reality! + if (null != crouton.getActivity() && null != crouton.getView() && null != crouton.getView().getParent()) { + ((ViewGroup) crouton.getView().getParent()).removeView(crouton.getView()); + + // remove any messages pending for the crouton + removeAllMessagesForCrouton(crouton); + } + // remove any matching croutons from queue + if (null != croutonQueue) { + final Iterator croutonIterator = croutonQueue.iterator(); + while (croutonIterator.hasNext()) { + final Crouton c = croutonIterator.next(); + if (c.equals(crouton) && (null != c.getActivity())) { + // remove the crouton from the content view + if (crouton.isShowing()) { + ((ViewGroup) c.getView().getParent()).removeView(c.getView()); + } + + // remove any messages pending for the crouton + removeAllMessagesForCrouton(c); + + // remove the crouton from the queue + croutonIterator.remove(); + + // we have found our crouton so just break + break; + } + } + } + } + + /** + * Removes all {@link Crouton}s from the queue. + */ + void clearCroutonQueue() { + removeAllMessages(); + + if (null != croutonQueue) { + // remove any views that may already have been added to the activity's + // content view + for (Crouton crouton : croutonQueue) { + if (crouton.isShowing()) { + ((ViewGroup) crouton.getView().getParent()).removeView(crouton.getView()); + } + } + croutonQueue.clear(); + } + } + + /** + * Removes all {@link Crouton}s for the provided activity. This will remove + * crouton from {@link Activity}s content view immediately. + */ + void clearCroutonsForActivity(Activity activity) { + if (null != croutonQueue) { + Iterator croutonIterator = croutonQueue.iterator(); + while (croutonIterator.hasNext()) { + Crouton crouton = croutonIterator.next(); + if ((null != crouton.getActivity()) && crouton.getActivity().equals(activity)) { + // remove the crouton from the content view + if (crouton.isShowing()) { + ((ViewGroup) crouton.getView().getParent()).removeView(crouton.getView()); + } + + removeAllMessagesForCrouton(crouton); + + // remove the crouton from the queue + croutonIterator.remove(); + } + } + } + } + + private void removeAllMessages() { + removeMessages(Messages.ADD_CROUTON_TO_VIEW); + removeMessages(Messages.DISPLAY_CROUTON); + removeMessages(Messages.REMOVE_CROUTON); + } + + private void removeAllMessagesForCrouton(Crouton crouton) { + removeMessages(Messages.ADD_CROUTON_TO_VIEW, crouton); + removeMessages(Messages.DISPLAY_CROUTON, crouton); + removeMessages(Messages.REMOVE_CROUTON, crouton); + + } + + /** + * Generates and dispatches an SDK-specific spoken announcement. + *

+ * For backwards compatibility, we're constructing an event from scratch + * using the appropriate event type. If your application only targets SDK + * 16+, you can just call View.announceForAccessibility(CharSequence). + *

+ *

+ * note: AccessibilityManager is only available from API lvl 4. + *

+ * Adapted from https://http://eyes-free.googlecode.com/files/accessibility_codelab_demos_v2_src.zip + * via https://github.com/coreform/android-formidable-validation + * + * @param context + * Used to get {@link AccessibilityManager} + * @param text + * The text to announce. + */ + public static void announceForAccessibilityCompat(Context context, CharSequence text) { + if (Build.VERSION.SDK_INT >= 4) { + AccessibilityManager accessibilityManager = (AccessibilityManager) context.getSystemService( + Context.ACCESSIBILITY_SERVICE); + if (!accessibilityManager.isEnabled()) { + return; + } + + // Prior to SDK 16, announcements could only be made through FOCUSED + // events. Jelly Bean (SDK 16) added support for speaking text verbatim + // using the ANNOUNCEMENT event type. + final int eventType; + if (Build.VERSION.SDK_INT < 16) { + eventType = AccessibilityEvent.TYPE_VIEW_FOCUSED; + } else { + eventType = AccessibilityEventCompat.TYPE_ANNOUNCEMENT; + } + + // Construct an accessibility event with the minimum recommended + // attributes. An event without a class name or package may be dropped. + final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); + event.getText().add(text); + event.setClassName(Manager.class.getName()); + event.setPackageName(context.getPackageName()); + + // Sends the event directly through the accessibility manager. If your + // application only targets SDK 14+, you should just call + // getParent().requestSendAccessibilityEvent(this, event); + accessibilityManager.sendAccessibilityEvent(event); + } + } +} diff --git a/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/Style.java b/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/Style.java new file mode 100644 index 00000000..28f2adaa --- /dev/null +++ b/external/Crouton/library/src/de/keyboardsurfer/android/widget/crouton/Style.java @@ -0,0 +1,539 @@ +/* + * Copyright 2012 - 2013 Benjamin Weiss + * Copyright 2012 Neofonie Mobile GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 de.keyboardsurfer.android.widget.crouton; + +import android.graphics.drawable.Drawable; +import android.view.Gravity; +import android.view.ViewGroup.LayoutParams; +import android.widget.ImageView; + + +/** + * The style for a {@link Crouton}. + */ + +public class Style { + + /** + * Display a {@link Crouton} for an infinite amount of time or + * until {@link de.keyboardsurfer.android.widget.crouton.Crouton#cancel()} has been called. + */ + public static final int DURATION_INFINITE = -1; + + /** + * Default style for alerting the user. + */ + public static final Style ALERT; + /** + * Default style for confirming an action. + */ + public static final Style CONFIRM; + /** + * Default style for general information. + */ + public static final Style INFO; + + public static final int holoRedLight = 0xffff4444; + public static final int holoGreenLight = 0xff99cc00; + public static final int holoBlueLight = 0xff33b5e5; + + static { + ALERT = new Builder().setDuration(5000).setBackgroundColorValue(holoRedLight).setHeight(LayoutParams.WRAP_CONTENT) + .build(); + CONFIRM = new Builder().setDuration(3000).setBackgroundColorValue(holoGreenLight).setHeight( + LayoutParams.WRAP_CONTENT).build(); + INFO = new Builder().setDuration(3000).setBackgroundColorValue(holoBlueLight).setHeight(LayoutParams.WRAP_CONTENT) + .build(); + } + + /** + * The durationInMilliseconds the {@link Crouton} will be displayed in + * milliseconds. + */ + final int durationInMilliseconds; + + /** + * The resource id of the backgroundResourceId. + *

+ * 0 for no backgroundResourceId. + */ + final int backgroundColorResourceId; + + /** + * The resource id of the backgroundDrawableResourceId. + *

+ * 0 for no backgroundDrawableResourceId. + */ + final int backgroundDrawableResourceId; + + /** + * The backgroundColorResourceValue's e.g. 0xffff4444; + *

+ * -1 for no value. + */ + final int backgroundColorValue; + + /** + * Whether we should isTileEnabled the backgroundResourceId or not. + */ + final boolean isTileEnabled; + + /** + * The text colorResourceId's resource id. + *

+ * 0 sets the text colorResourceId to the system theme default. + */ + final int textColorResourceId; + + /** + * The height of the {@link Crouton} in pixels. + */ + final int heightInPixels; + + /** + * Resource ID for the height of the {@link Crouton}. + */ + final int heightDimensionResId; + + /** + * The width of the {@link Crouton} in pixels. + */ + final int widthInPixels; + + /** + * Resource ID for the width of the {@link Crouton}. + */ + final int widthDimensionResId; + + /** + * The text's gravity as provided by {@link Gravity}. + */ + final int gravity; + + /** + * An additional image to display in the {@link Crouton}. + */ + final Drawable imageDrawable; + + /** + * An additional image to display in the {@link Crouton}. + */ + final int imageResId; + + /** + * The {@link ImageView.ScaleType} for the image to display in the + * {@link Crouton}. + */ + final ImageView.ScaleType imageScaleType; + + /** + * The text size in sp + *

+ * 0 sets the text size to the system theme default + */ + final int textSize; + + /** + * The text shadow color's resource id + */ + final int textShadowColorResId; + + /** + * The text shadow radius + */ + final float textShadowRadius; + + /** + * The text shadow vertical offset + */ + final float textShadowDy; + + /** + * The text shadow horizontal offset + */ + final float textShadowDx; + + /** + * The text appearance resource id for the text. + */ + final int textAppearanceResId; + + /** + * The resource id for the in animation + */ + final int inAnimationResId; + + /** + * The resource id for the out animation + */ + final int outAnimationResId; + + /** + * The padding for the crouton view content in pixels + */ + final int paddingInPixels; + + /** + * The resource id for the padding for the view content + */ + final int paddingDimensionResId; + + private Style(final Builder builder) { + this.durationInMilliseconds = builder.durationInMilliseconds; + this.backgroundColorResourceId = builder.backgroundColorResourceId; + this.backgroundDrawableResourceId = builder.backgroundDrawableResourceId; + this.isTileEnabled = builder.isTileEnabled; + this.textColorResourceId = builder.textColorResourceId; + this.heightInPixels = builder.heightInPixels; + this.heightDimensionResId = builder.heightDimensionResId; + this.widthInPixels = builder.widthInPixels; + this.widthDimensionResId = builder.widthDimensionResId; + this.gravity = builder.gravity; + this.imageDrawable = builder.imageDrawable; + this.textSize = builder.textSize; + this.textShadowColorResId = builder.textShadowColorResId; + this.textShadowRadius = builder.textShadowRadius; + this.textShadowDx = builder.textShadowDx; + this.textShadowDy = builder.textShadowDy; + this.textAppearanceResId = builder.textAppearanceResId; + this.inAnimationResId = builder.inAnimationResId; + this.outAnimationResId = builder.outAnimationResId; + this.imageResId = builder.imageResId; + this.imageScaleType = builder.imageScaleType; + this.paddingInPixels = builder.paddingInPixels; + this.paddingDimensionResId = builder.paddingDimensionResId; + this.backgroundColorValue = builder.backgroundColorValue; + } + + /** + * Builder for the {@link Style} object. + */ + public static class Builder { + private int durationInMilliseconds; + private int backgroundColorValue; + private int backgroundColorResourceId; + private int backgroundDrawableResourceId; + private boolean isTileEnabled; + private int textColorResourceId; + private int heightInPixels; + private int heightDimensionResId; + private int widthInPixels; + private int widthDimensionResId; + private int gravity; + private Drawable imageDrawable; + private int textSize; + private int textShadowColorResId; + private float textShadowRadius; + private float textShadowDx; + private float textShadowDy; + private int textAppearanceResId; + private int inAnimationResId; + private int outAnimationResId; + private int imageResId; + private ImageView.ScaleType imageScaleType; + private int paddingInPixels; + private int paddingDimensionResId; + + public Builder() { + durationInMilliseconds = 3000; + paddingInPixels = 10; + backgroundColorResourceId = android.R.color.holo_blue_light; + backgroundDrawableResourceId = 0; + backgroundColorValue = -1; + isTileEnabled = false; + textColorResourceId = android.R.color.white; + heightInPixels = LayoutParams.WRAP_CONTENT; + widthInPixels = LayoutParams.MATCH_PARENT; + gravity = Gravity.CENTER; + imageDrawable = null; + inAnimationResId = 0; + outAnimationResId = 0; + imageResId = 0; + imageScaleType = ImageView.ScaleType.FIT_XY; + } + + /** + * Set the durationInMilliseconds option of the {@link Crouton}. + * + * @param duration + * The durationInMilliseconds the crouton will be displayed + * {@link Crouton} in milliseconds. + * @return the {@link Builder}. + */ + public Builder setDuration(int duration) { + this.durationInMilliseconds = duration; + + return this; + } + + /** + * Set the backgroundColorResourceId option of the {@link Crouton}. + * + * @param backgroundColorResourceId + * The backgroundColorResourceId's resource id. + * @return the {@link Builder}. + */ + public Builder setBackgroundColor(int backgroundColorResourceId) { + this.backgroundColorResourceId = backgroundColorResourceId; + + return this; + } + + /** + * Set the backgroundColorResourceValue option of the {@link Crouton}. + * + * @param backgroundColorValue + * The backgroundColorResourceValue's e.g. 0xffff4444; + * @return the {@link Builder}. + */ + public Builder setBackgroundColorValue(int backgroundColorValue) { + this.backgroundColorValue = backgroundColorValue; + return this; + } + + /** + * Set the backgroundDrawableResourceId option for the {@link Crouton}. + * + * @param backgroundDrawableResourceId + * Resource ID of a backgroundDrawableResourceId image drawable. + * @return the {@link Builder}. + */ + public Builder setBackgroundDrawable(int backgroundDrawableResourceId) { + this.backgroundDrawableResourceId = backgroundDrawableResourceId; + + return this; + } + + /** + * Set the heightInPixels option for the {@link Crouton}. + * + * @param height + * The height of the {@link Crouton} in pixel. Can also be + * {@link LayoutParams#MATCH_PARENT} or + * {@link LayoutParams#WRAP_CONTENT}. + * @return the {@link Builder}. + */ + public Builder setHeight(int height) { + this.heightInPixels = height; + + return this; + } + + /** + * Set the resource id for the height option for the {@link Crouton}. + * + * @param heightDimensionResId + * Resource ID of a dimension for the height of the {@link Crouton}. + * @return the {@link Builder}. + */ + public Builder setHeightDimensionResId(int heightDimensionResId) { + this.heightDimensionResId = heightDimensionResId; + + return this; + } + + /** + * Set the widthInPixels option for the {@link Crouton}. + * + * @param width + * The width of the {@link Crouton} in pixel. Can also be + * {@link LayoutParams#MATCH_PARENT} or + * {@link LayoutParams#WRAP_CONTENT}. + * @return the {@link Builder}. + */ + public Builder setWidth(int width) { + this.widthInPixels = width; + + return this; + } + + /** + * Set the resource id for the width option for the {@link Crouton}. + * + * @param widthDimensionResId + * Resource ID of a dimension for the width of the {@link Crouton}. + * @return the {@link Builder}. + */ + public Builder setWidthDimensionResId(int widthDimensionResId) { + this.widthDimensionResId = widthDimensionResId; + + return this; + } + + /** + * Set the isTileEnabled option for the {@link Crouton}. + * + * @param isTileEnabled + * true if you want the backgroundResourceId to be + * tiled, else false. + * @return the {@link Builder}. + */ + public Builder setTileEnabled(boolean isTileEnabled) { + this.isTileEnabled = isTileEnabled; + + return this; + } + + /** + * Set the textColorResourceId option for the {@link Crouton}. + * + * @param textColor + * The resource id of the text colorResourceId. + * @return the {@link Builder}. + */ + public Builder setTextColor(int textColor) { + this.textColorResourceId = textColor; + + return this; + } + + /** + * Set the gravity option for the {@link Crouton}. + * + * @param gravity + * The text's gravity as provided by {@link Gravity}. + * @return the {@link Builder}. + */ + public Builder setGravity(int gravity) { + this.gravity = gravity; + + return this; + } + + /** + * Set the image option for the {@link Crouton}. + * + * @param imageDrawable + * An additional image to display in the {@link Crouton}. + * @return the {@link Builder}. + */ + public Builder setImageDrawable(Drawable imageDrawable) { + this.imageDrawable = imageDrawable; + + return this; + } + + /** + * Set the image resource option for the {@link Crouton}. + * + * @param imageResId + * An additional image to display in the {@link Crouton}. + * @return the {@link Builder}. + */ + public Builder setImageResource(int imageResId) { + this.imageResId = imageResId; + + return this; + } + + /** + * The text size in sp + */ + public Builder setTextSize(int textSize) { + this.textSize = textSize; + return this; + } + + /** + * The text shadow color's resource id + */ + public Builder setTextShadowColor(int textShadowColorResId) { + this.textShadowColorResId = textShadowColorResId; + return this; + } + + /** + * The text shadow radius + */ + public Builder setTextShadowRadius(float textShadowRadius) { + this.textShadowRadius = textShadowRadius; + return this; + } + + /** + * The text shadow horizontal offset + */ + public Builder setTextShadowDx(float textShadowDx) { + this.textShadowDx = textShadowDx; + return this; + } + + /** + * The text shadow vertical offset + */ + public Builder setTextShadowDy(float textShadowDy) { + this.textShadowDy = textShadowDy; + return this; + } + + /** + * The text appearance resource id for the text. + */ + public Builder setTextAppearance(int textAppearanceResId) { + this.textAppearanceResId = textAppearanceResId; + return this; + } + + /** + * The resource id for the in animation + */ + public Builder setInAnimation(int inAnimationResId) { + this.inAnimationResId = inAnimationResId; + return this; + } + + /** + * The resource id for the out animation + */ + public Builder setOutAnimation(int outAnimationResId) { + this.outAnimationResId = outAnimationResId; + return this; + } + + /** + * The {@link android.widget.ImageView.ScaleType} for the image + */ + public Builder setImageScaleType(ImageView.ScaleType imageScaleType) { + this.imageScaleType = imageScaleType; + return this; + } + + /** + * The padding for the crouton view's content in pixels + */ + public Builder setPaddingInPixels(int padding) { + this.paddingInPixels = padding; + return this; + } + + /** + * The resource id for the padding for the crouton view's content + */ + public Builder setPaddingDimensionResId(int paddingResId) { + this.paddingDimensionResId = paddingResId; + return this; + } + + /** + * @return a configured {@link Style} object. + */ + public Style build() { + return new Style(this); + } + } +} diff --git a/external/Crouton/pom.xml b/external/Crouton/pom.xml new file mode 100644 index 00000000..a56203e5 --- /dev/null +++ b/external/Crouton/pom.xml @@ -0,0 +1,102 @@ + + + + + + 4.0.0 + + Crouton Parent + crouton-parent + 1.7 + de.keyboardsurfer.android.widget + pom + + + + keyboardsurfer + Benjamin Weiss + + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + git@github.com:keyboardsurfer/Crouton.git + scm:git:git@github.com:keyboardsurfer/Crouton.git + scm:git:git@github.com:keyboardsurfer/Crouton.git + + + + library + sample + + + + UTF-8 + 4.1.1.4 + 16 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + 1.6 + 1.6 + + + + org.apache.maven.plugins + maven-source-plugin + 2.2 + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9 + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + 3.4.1 + true + + + ${android.version.platform} + + true + true + + + + + + +