Initial commit

This commit is contained in:
Felix Ableitner 2012-10-09 08:17:04 -07:00
commit e31eff3d25
65 changed files with 3828 additions and 0 deletions

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
# built application files
*.apk
*.ap_
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties

16
README.md Normal file
View File

@ -0,0 +1,16 @@
ReleaseTracker
==============
An app I made to learn Android programming. Development was stopped when my phone broke.
I only recently fixed some leftover bugs, but don't expect everything to work.
It keeps track of future movies and notifies the user when a selected movie is released.
A [TMDB](http://themoviedb.org/) API key has to be placed in `res/values/values.xml` value `api_key_tmdb`.
## Dependencies
* actionbarsherlock
* jtmdb
## License
[Modified BSD License](http://directory.fsf.org/wiki/License:BSD_3Clause)

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/title"
android:text="@string/addcustom_title" />
<EditText
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/title_text"
android:layout_alignParentTop="true"
android:ems="10"
android:inputType="text" >
</EditText>
<TextView
android:id="@+id/artist_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/title_text"
android:layout_alignBaseline="@+id/artist"
android:text="@string/addcustom_artist" />
<EditText
android:id="@+id/artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/title"
android:layout_alignLeft="@+id/title"
android:ems="10"
android:inputType="text" >
</EditText>
<DatePicker
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/artist"
android:layout_toRightOf="@+id/artist_text"
android:calendarViewShown="false" />
<Button
android:id="@+id/submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@+id/date"
android:text="@string/addcustom_submit" />
</RelativeLayout>

35
res/layout/add_item.xml Normal file
View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- from: https://github.com/JakeWharton/ActionBarSherlock/blob/master/samples/fragments/res/layout/fragment_tabs_pager.xml -->
<TabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TabWidget
android:id="@android:id/tabs"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"/>
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="0"/>
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
</TabHost>

21
res/layout/history.xml Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
<TextView
android:id="@+id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_centerInParent="true"
android:text="@string/history_empty"
android:gravity="center" />
</RelativeLayout>

20
res/layout/home.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_centerInParent="true"
android:text="@string/home_empty"
android:gravity="center" />
</RelativeLayout>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/RelativeLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:textSize="30dp" />
<TextView
android:id="@+id/release_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/title"
android:layout_toRightOf="@+id/artist"
android:layout_toLeftOf="@+id/checkbox"
android:gravity="right" />
<TextView
android:id="@+id/artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/title" />
<CheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:onClick="checkBoxClick"
android:visibility="invisible"
android:focusable="false" />
</RelativeLayout>

42
res/layout/search.xml Normal file
View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<EditText
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@+id/button"
android:ems="10"
android:inputType="text" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="@string/search_button_text" />
<!-- android:onClick="search" -->
<ListView
android:id="@+id/results"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/text" />
<TextView
android:id="@+id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_centerInParent="true"
android:text="@string/search_list_empty"
android:gravity="center" />
</RelativeLayout>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@color/selector_pressed" />
<item android:state_focused="true" android:drawable="@color/selector_focused" />
<item android:state_selected="true" android:drawable="@color/selector_focused" />
<item android:state_pressed="false" android:state_focused="false" android:drawable="@color/selector_default" />
<item android:drawable="@color/transparent" />
</selector>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- embed icons -->
<item android:id="@+id/add_item"
android:title="@string/additem_title"
android:icon="@drawable/ic_menu_add"
android:showAsAction="ifRoom" />
<item android:id="@+id/history"
android:title="@string/history_title"
android:icon="@drawable/ic_menu_recent_history"
android:showAsAction="ifRoom" />
<item android:id="@+id/persons"
android:title="@string/persons_title"
android:icon="@drawable/ic_menu_persons"
android:showAsAction="ifRoom" />
<item android:id="@+id/preferences"
android:title="@string/preferences_title"
android:icon="@drawable/ic_menu_preferences"
android:showAsAction="ifRoom" />
</menu>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@+id/add_item"
android:title="@string/additem_title"
android:icon="@drawable/ic_menu_add"
android:showAsAction="ifRoom" />
</menu>

11
res/raw/changelog.txt Normal file
View File

@ -0,0 +1,11 @@
<html>
<head>
</head>
<body>
$ 0.1
% Version 0.1.0
_ 2012-04-01
* Initial release
$ END_OF_CHANGE_LOG
</body>
</html>

7
res/raw/first_start.txt Normal file
View File

@ -0,0 +1,7 @@
Welcome!
This app allows you to choose movies you wish to watch when they are released.
When one of the movies in your list is released, a notification will be shown, informing you about its release.
Please use the contact feature in the application preferences for any bugs or improvements.

51
res/raw/license_lgpl.txt Normal file
View File

@ -0,0 +1,51 @@
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, “this License” refers to version 3 of the GNU Lesser General Public License, and the “GNU GPL” refers to version 3 of the GNU General Public License.
“The Library” refers to a covered work governed by this License, other than an Application or a Combined Work as defined below.
An “Application” is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library.
A “Combined Work” is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the “Linked Version”.
The “Minimal Corresponding Source” for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version.
The “Corresponding Application Code” for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version:
a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following:
a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license document.
c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.
1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version.
e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.

86
res/raw/license_tmdb.txt Normal file
View File

@ -0,0 +1,86 @@
Terms of Use
Thank you for using the TMDb application programming interfaces (the "TMDB APIs"). By using the TMDb APIs, you UNCONDITIONALLY CONSENT AND AGREE TO BE BOUND BY AND A PARTY TO THESE TERMS AND CONDITIONS. If you disagree with any of these terms, TMDb does not grant you a license to use the TMDb APIs. TMDb reserves the right, in its sole discretion to modify this Agreement at any time by posting a notice to themoviedb.org. You shall be responsible for reviewing and becoming familiar with any such modification. Such modifications are effective upon first posting or notification and use of the TMDb API by Licensee following any such notification constitutes Licensee's acceptance of the terms and conditions of this Agreement as modified. You can always find the most recent version of these terms here.
Your license to the TMDb APIs under these terms continues until either party terminates it. You may terminate the license by discontinuing use of all or any of the TMDb APIs. TMDb may terminate the license at any time for any reason. Your rights to use the TMDb APIs terminate automatically if (i) you violate any of these terms, (ii) TMDb publicly posts a written notice of termination on themoviedb.org, (iii) TMDb sends a written notice of termination to you (via electronic or other means), or (iv) TMDb disables access to the TMDb APIs to you. If TMDb terminates your license, or you terminate your license, you must immediately cease all use of TMDb content.
1. Licensed Uses and Restrictions
The TMDb APIs are owned by TMDb, Inc. (hereinafter "TMDb") and are licensed to you on a worldwide (except as limited below), non-exclusive, non-transferable, non-sublicenseable basis on the terms and conditions set forth herein. These terms define legal use of the TMDb APIs, all updates, revisions, substitutions that may be made available by TMDb, and any copies of the TMDb APIs made by or for you. TMDb reserves all rights not expressly granted to you.
A. YOU SHALL
Comply with the current TMDb Terms of Use at http://www.themoviedb.org/terms-of-use.
You shall give TMDb attribution for any and all content used from the site pursuant to paragraph 3.
B. YOU SHALL NOT
Attempt to cloak or conceal your identity or your application's identity when requesting authorization to use TMDb APIs.
Use (or create applications that use) an unreasonable amount of bandwidth.
Cache any TMDb metadata or photos other than for reasonable periods in order to provide your service.
Use TMDb APIs for any application that constitutes, promotes or is used in connection with materials that constitute, promote or are used primarily for the purpose of dealing in: spyware, adware, or other malicious programs or code, counterfeit goods, items subject to U.S. embargo, unsolicited mass distribution of email ("spam"), multi-level marketing proposals, hate materials, hacking/surveillance/interception/descrambling equipment, libelous, defamatory, obscene, pornographic, abusive or otherwise offensive content.
Use TMDb APIs in any manner or for any purpose that violates any law or regulation, any right of any person, including but not limited to intellectual property rights, rights of privacy, or rights of personality.
Use TMDb as a generic image hosting service for banner advertisements, graphics, etc.
Use TMDb APIs in a manner that adversely impacts the stability of themoviedb.org servers or adversely impacts the behavior of other applications using the TMDb APIs.
Sell, lease, or sublicense TMDb APIs or access thereto or derive revenues from the use or provision of TMDb APIs, whether for direct commercial or monetary gain or otherwise, except as set forth below.
Use TMDb APIs in a manner that is confusing or misleading as to the source or origin of your application or site.
2. Commercial Use
A. RULES AND RESTRICTIONS
If the primary purpose of your application is to derive revenue, it is considered a commercial application. TMDb reserves the right to make these evaluations at the time that you apply for the license. TMDb may also monitor your site or application over time to ensure continued compliance with the appropriate type of API key.
If you're in doubt about whether your application is commercial, here are a few common examples of commercial use that may provide you some guidance:
Users are charged a fee for your product or a 3rd party's product or service or a 3rd party's service that includes some sort of integration using the TMDb APIs.
You sell services using TMDb's APIs to bring users' TMDb content into your service.
Your site is a "destination" site that uses TMDb content to drive traffic and generate revenue.
Your site generates revenue by charging users for access to content related to TMDb content such as movies, television shows and music.
B. APPLICATION FOR A COMMERCIAL API KEY
If you want to apply for a commercial API key, go to the following form to see what information you need to provide. Lack of specificity or supporting information could delay your application indefinitely, please be very clear in defining your usage. It is always better to include more detail about your application. Please note that in some cases we may grant your request for a commercial API key subject to your payment of fees, such as to help cover infrastructure costs.
3. Attribution
You shall use the TMDb logo to identify your use of the TMDb APIs.
You shall place the following notice prominently on your application: "This product uses the TMDb API but is not endorsed or certified by TMDb."
Any use of the TMDb logo in your application shall be less prominent than the logo or mark that primarily describes the application and your use of the TMDb logo shall not imply any endorsement by TMDb.
4. Ownership and Relationship of Parties
The TMDb APIs may be protected by copyrights, trademarks, service marks, international treaties, and/or other proprietary rights and laws of the U.S. and other countries. TMDb's rights apply to the TMDb APIs and all output and executables of the TMDb APIs, excluding any software components developed by you, which do not themselves, incorporate the TMDb APIs or any output or executables of the TMDb APIs. You agree to abide by all applicable proprietary rights laws and other laws, as well as any additional copyright notices or restrictions contained in these terms. TMDb owns all rights, title, and interest in and to the TMDb APIs. Except as expressly set forth in Section 1, these terms grant you no right, title, or interest in any intellectual property owned or licensed by TMDb, including (but not limited to) the TMDb APIs and TMDb trademarks.
5. Support
TMDb may elect to provide you with support or modifications for the TMDb APIs (collectively, "Support"), in its sole discretion, and may terminate such Support at any time without notice to you. TMDb may change, suspend, or discontinue any aspect of the TMDb APIs at any time, including the availability of any TMDb APIs. TMDb may also impose limits on certain features and services or restrict your access to parts or all of the TMDb APIs or the TMDb Web site without notice or liability. TMDb may require at any time that you use only the most recent update or version of the TMDb APIs released by TMDb.
6. Fees and Payments
TMDb is committed to free and open access to our APIs for commercial and non-commercial purposes. However, providing the APIs does have real costs for TMDb. For uses of TMDb APIs over a certain rate or for certain types of commercial applications, TMDb reserves the right to charge fees for future use of or access to the TMDb APIs.
7. Disclaimer of Any Warranty
SOME OF THE TMDb APIS MAY BE EXPERIMENTAL AND NOT TESTED IN ANY MANNER. TMDb DOES NOT REPRESENT OR WARRANT THAT ANY TMDb APIS ARE FREE OF INACCURACIES, ERRORS, BUGS, OR INTERRUPTIONS, OR ARE RELIABLE, ACCURATE, COMPLETE, OR OTHERWISE VALID.
THE TMDb APIS ARE PROVIDED "AS IS" WITH NO WARRANTY, EXPRESS OR IMPLIED, OF ANY KIND AND TMDb EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES AND CONDITIONS, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AVAILABLILTIY, SECURITY, TITLE AND/OR NON-INFRINGEMENT. SOME STATES DO NOT ALLOW LIMITATIONS ON IMPLIED WARRANTIES, SO THE ABOVE LIMITATION MAY NOT APPLY TO YOU. YOU MAY HAVE ADDITIONAL RIGHTS THAT VARY FROM STATE TO STATE.
YOUR USE OF TMDb APIS IS AT YOUR OWN DISCRETION AND RISK, AND YOU WILL BE SOLELY RESPONSIBLE FOR ANY DAMAGE THAT RESULTS FROM THE USE OF ANY TMDb APIS INCLUDING, BUT NOT LIMITED TO, ANY DAMAGE TO YOUR COMPUTER SYSTEM OR LOSS OF DATA.
8. Limitation of Liability
REGARDLESS OF WHETHER ANY REMEDY SET FORTH HEREIN FAILS OF ITS ESSENTIAL PURPOSE OR OTHERWISE, AND EXCEPT FOR BODILY INJURY, IN NO EVENT WILL TMDB OR ITS LICENSORS OR SUPPLIERS BE LIABLE TO YOU OR TO ANY THIRD PARTY UNDER ANY TORT, CONTRACT, NEGLIGENCE, COPYRIGHT INFRINGEMENT LIABILTY, STRICT LIABILITY OR OTHER LEGAL OR EQUITABLE THEORY FOR ANY LOST PROFITS, LOST OR CORRUPTED DATA, COMPUTER FAILURE OR MALFUNCTION, INTERRUPTION OF BUSINESS, OR OTHER SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND ARISING OUT OF THE USE OR INABILITY TO USE THE API, EVEN IF TMDb HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES AND WHETHER OR NOT SUCH LOSS OR DAMAGES ARE FORESEEABLE. SOME STATES DO NOT ALLOW LIMITATIONS ON LIABILITY, SO THE ABOVE LIMITATION MAY NOT APPLY TO YOU.
9. Release and Waiver
To the maximum extent permitted by applicable law, you hereby release and waive all claims against TMDb, and its subsidiaries, affiliates, officers, agents, licensors, co-branders or other partners, and employees from any and all liability for claims, damages (actual and/or consequential), costs and expenses (including litigation costs and attorneys' fees) of every kind and nature, arising from or in any way related to your use of TMDb APIs. If you are a California resident, you waive your rights under California Civil Code § 1542, which states, "A general release does not extend to claims which the creditor does not know or suspect to exist in his favor at the time of executing the release, which if known by him must have materially affected his settlement with the debtor." You understand that any fact relating to any matter covered by this release may be found to be other than now believed to be true and you accept and assume the risk of such possible differences in fact. In addition, you expressly waive and relinquish any and all rights and benefits that you may have under any other state or federal statute or common law principle of similar effect, to the fullest extent permitted by law.
10. Hold Harmless and Indemnity
To the maximum extent permitted by applicable law, you agree to hold harmless and indemnify TMDb and its subsidiaries, affiliates, officers, agents, licensors, co-branders or other partners, and employees from and against any third party claim arising from or in any way related to your use of TMDb APIs, including any liability or expense arising from all claims, losses, damages (actual and/or consequential), suits, judgments, litigation costs and attorneys' fees, of every kind and nature. TMDb shall use good faith efforts to provide you with written notice of such claim, suit or action.
11. General Terms
Relationship of the Parties. Notwithstanding any provision hereof, for all purposes of the Terms of Use, you and TMDb shall be and act independently and not as partner, joint venturer, agent, employee or employer of the other. You shall not have any authority to assume or create any obligation for or on behalf of TMDb, express or implied, and you shall not attempt to bind TMDb to any contract.
Invalidity of Specific Terms. If any provision of the Terms of Use is found by a court of competent jurisdiction to be invalid, the parties nevertheless agree that the other provisions of this agreement will remain in full force and effect and the court should endeavor to give effect to the parties' intentions as reflected in the invalid provision.
Location of Lawsuit and Choice of Law. THE TERMS OF USE AND THE RELATIONSHIP BETWEEN YOU AND TMDB SHALL BE GOVERNED BY THE LAWS OF THE STATE OF CALIFORNIA WITHOUT REGARD TO ITS CONFLICT OF LAW PROVISIONS. YOU AND TMDB AGREE TO SUBMIT TO THE PERSONAL JURISDICTION OF THE COURTS LOCATED WITHIN THE COUNTY OF SAN MATEO, CALIFORNIA.
No Waiver of Rights by TMDb. TMDb's failure to exercise or enforce any right or provision of the Terms of Use shall not constitute a waiver of such right or provision.
No Transfer. The rights and obligations of these Terms of Use is personal to you and may not be transferred by you, either voluntarily or by operation of law.
Notice. Any notice to be sent to you under these Terms of Use may be sent via email, post, or any other reasonable means, at the contact information provided by you to TMDb from time to time. It is your obligation to insure that this information is current.
Miscellaneous. The section headings and subheadings contained in this agreement are included for convenience only, and shall not limit or otherwise affect the terms of the Terms of Use. Any construction or interpretation to be made of the Terms of Use shall not be construed against the drafter. The Terms of Use constitute the entire agreement between TMDb and you with respect to the subject matter hereof.

7
res/values/colors.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="transparent">#1000</color>
<color name="selector_default">#c5eaf8</color>
<color name="selector_focused">#8ad5f0</color>
<color name="selector_pressed">#50c0e9</color>
</resources>

91
res/values/strings.xml Normal file
View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- General -->
<string name="app_name">Release Tracker</string>
<string name="error_require_internet">Error: An active internet connection is required for this action.</string>
<!-- Changelog -->
<string name="changelog_full_title">Change Log</string>
<string name="changelog_title">What\'s New</string>
<string name="changelog_show_full">Show Full Change Log</string>
<!-- Context Menu -->
<string name="contextmenu_delete">Delete item</string>
<string name="contextmenu_link_open">Open Link</string>
<!-- Notification Service -->
<string name="notification_released_single">New item released.</string>
<string name="notification_released_multi">New items released.</string>
<!-- Home -->
<string name="home_menu_search">Search Items</string>
<string name="home_menu_history">History</string>
<string name="home_menu_preferences">Preferences</string>
<string name="home_empty">You don\'t have any items yet. \n Use Menu -> Add Item to insert a new one.</string>
<string name="home_delete_item">Do you really want to delete this item?</string>
<string name="home_update_data">Update data</string>
<string name="home_welcome">Welcome</string>
<!-- Add Item -->
<string name="additem_title">Add Item</string>
<string name="additem_search">Search</string>
<string name="additem_custom">Custom Item</string>
<string name="additem_added">Item added.</string>
<!-- Add Person -->
<string name="addperson_title">Add Person</string>
<!-- other values are taken from additem -->
<!-- Search -->
<string name="search_button_text">Search</string>
<string name="search_add_item">Do you want to add this item to your list?</string>
<string name="search_list_empty">No matches</string>
<!-- Add Custom Item -->
<string name="addcustom_title">Title</string>
<string name="addcustom_artist">Artist</string>
<string name="addcustom_submit">Add Item</string>
<string name="addcustom_missing_title">Please enter a title.</string>
<!-- History -->
<string name="history_title">History</string>
<string name="history_empty">No items found.</string>
<!-- Persons -->
<string name="persons_title">Persons</string>
<string name="persons_added">Person added.</string>
<!-- Preferences -->
<string name="preferences_title">Preferences</string>
<string name="preferences_category_general">General</string>
<string name="preferences_item_data_updates_title">Enable automatic data updates</string>
<string name="preferences_item_data_updates_message">Select interval to check for updated item data and newly announced items.</string>
<string-array name="preferences_item_data_updates_list_items">
<item>Never</item>
<item>Daily</item>
<item>Weekly</item>
<item>Every two weeks</item>
<item>Monthly</item>
</string-array>
<string name="preferences_item_updates_wifi_title">Use Wifi only</string>
<string name="preferences_item_updates_wifi_summary">Disable automatic updates via mobile data?</string>
<string name="preferences_screen_about">About/Licences</string>
<string name="preferences_category_app">Application</string>
<string name="preferences_item_version">Version</string>
<string name="preferences_item_changelog">View Changelog</string>
<string name="preferences_item_contact">Contact Developer</string>
<string name="preferences_item_website">Visit Application Website</string>
<string name="preferences_category_licences_opensource">Open Source Licences</string>
<string name="preferences_license_android_change_log">Android-Change-Log</string>
<string name="preferences_license_jtmdb">JTMDB</string>
<string name="preferences_category_licences_api">API Licences</string>
<string name="preferences_license_tmdb">TMDB</string>
</resources>

27
res/values/values.xml Normal file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- General -->
<string name="app_version_name">0.1.0</string>
<!-- API Keys -->
<!-- TODO: Insert API key here -->
<string name="api_key_tmdb"></string>
<!-- Web Links -->
<string name="api_tmdb_url_movie">http://www.themoviedb.org/movie/</string>
<string name="api_tmdb_url_person">http://www.themoviedb.org/person/</string>
<!-- Web Link Parameters -->
<string name="api_tmdb_param_language">\\?language\\=</string>
<!-- Preferences -->
<string-array name="preferences_item_data_updates_list_values">
<item>-1</item>
<item>1</item>
<item>7</item>
<item>14</item>
<item>30</item>
</string-array>
</resources>

71
res/xml/preferences.xml Normal file
View File

@ -0,0 +1,71 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:persistent="true">
<PreferenceCategory
android:title="@string/preferences_category_general">
<ListPreference
android:key="automatic_data_updates"
android:title="@string/preferences_item_data_updates_title"
android:summary="@string/preferences_item_data_updates_message"
android:entries="@array/preferences_item_data_updates_list_items"
android:entryValues="@array/preferences_item_data_updates_list_values"
android:defaultValue="7" />
<CheckBoxPreference
android:key="automatic_data_updates_wifi_only"
android:title="@string/preferences_item_updates_wifi_title"
android:summary="@string/preferences_item_updates_wifi_summary"
android:defaultValue="true" />
</PreferenceCategory>
<!-- item release notifications (time, type) -->
<!-- location/country for apis -->
<PreferenceScreen
android:title="@string/preferences_screen_about">
<PreferenceCategory
android:title="@string/preferences_category_app">
<Preference
android:title="@string/preferences_item_version"
android:summary="@string/app_version_name"
android:enabled="false" />
<Preference
android:key="changelog_view"
android:title="@string/preferences_item_changelog" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/preferences_category_licences_opensource">
<Preference
android:title="@string/preferences_license_android_change_log"
android:key="license_android_change_log">
</Preference>
<Preference
android:title="@string/preferences_license_jtmdb"
android:key="license_jtmdb">
</Preference>
<!-- actionbarsherlock / apache -->
</PreferenceCategory>
<PreferenceCategory
android:title="@string/preferences_category_licences_api">
<Preference
android:title="@string/preferences_license_tmdb"
android:key="license_tmdb">
</Preference>
</PreferenceCategory>
</PreferenceScreen>
</PreferenceScreen>

View File

@ -0,0 +1,218 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import org.json.JSONException;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.github.nutomic.releasetracker.sql.SqlCursorAdapter;
import com.github.nutomic.releasetracker.sql.SqlHelper;
import com.github.savvasdalkitsis.jtmdb.GeneralSettings;
import com.github.savvasdalkitsis.jtmdb.Movie;
/**
* This class searches on all APIs (one at a time -> pass object that contains
* type and query).
*/
public class ApiSearch extends
AsyncTask<String, Integer, ArrayAdapter<SqlItem>> {
private static final String TAG = "ApiSearch";
private final Activity activity_;
private ProgressDialog pg_;
private ArrayAdapter<SqlItem> adapter_;
/**
* Constructor, get activity to display ProgressDialog.
*
* @param activity
* Activity this class is constructed from.
*/
public ApiSearch(Activity activity) {
activity_ = activity;
}
/**
* Creates ProgressDialog before starting web request.
*/
@Override
protected void onPreExecute() {
pg_ = ProgressDialog.show(activity_, "", "Loading. Please wait...",
true, true, new OnCancelListener() {
public void onCancel(DialogInterface di) {
ApiSearch.this.cancel(true);
}
});
pg_.show();
}
/**
* Searches remote API for query, depending on selected type. Must have
* exactly two parameters. May return null (valid adapter value for
* ListView).
*
* @param params
* [0] Search query.
* @param params
* [1] Search type.
*/
@Override
protected ArrayAdapter<SqlItem> doInBackground(String... params) {
List<SqlItem> ri = new ArrayList<SqlItem>();
switch (Integer.parseInt(params[1])) {
case SqlHelper.API_ALL:
break;
case SqlHelper.API_MOVIE:
GeneralSettings.setAPILocale(Locale.getDefault());
GeneralSettings.setApiKey((String) activity_
.getText(R.string.api_key_tmdb));
List<Movie> movies;
try {
movies = Movie.search(params[0]);
} catch (IOException e) {
Log.w(TAG, "TMDB search failed.", e);
return null;
} catch (JSONException e) {
Log.w(TAG, "TMDB json read failed.", e);
return null;
}
Date now = new Date();
for (int i = 0; i < movies.size(); i++) {
if (isCancelled())
return null;
Movie cur = movies.get(i);
// ReleaseDate can be null, we don't want these items.
if ((cur.getReleasedDate() != null)
&& cur.getReleasedDate().after(now)) {
// try to get regisseur at this point?
ri.add(new SqlItem(cur.getName(), "",
cur.getReleasedDate(),
Integer.toString(cur.getID()), SqlHelper.API_MOVIE,
false, false));
}
}
break;
}
Collections.sort(ri, new Comparator<SqlItem>() {
/**
* Compare two release items by date. Does not check for null!
*
* @inheritDoc
*/
public int compare(SqlItem lhs, SqlItem rhs) {
SqlItem l = (SqlItem) lhs;
SqlItem r = (SqlItem) rhs;
if (l.releaseDate.before(r.releaseDate))
return -1;
if (l.releaseDate.after(r.releaseDate))
return 1;
return 0;
}
});
return new ArrayAdapter<SqlItem>(activity_, 0, ri) {
@Override
/**
* Create or initialize view with custom texts
* @inheritDoc
*/
public View getView(int position, View row,
android.view.ViewGroup parent) {
if (row == null) {
row = activity_.getLayoutInflater().inflate(
R.layout.listview_item, null);
}
SqlItem item = (SqlItem) getItem(position);
SqlCursorAdapter.initializeRow(row, item.title,
(String) SqlCursorAdapter.dateFormat(item.releaseDate),
item.artist);
return row;
};
};
}
/**
* Closes ProgressDialog and add all items into ListView.
*
* @param adapter
* Contains search results.
*/
@Override
protected void onPostExecute(ArrayAdapter<SqlItem> adapter) {
adapter_ = adapter;
ListView lv_ = (ListView) activity_.findViewById(R.id.results);
lv_.setAdapter(adapter_);
pg_.dismiss();
}
/**
* Search cancelled, hide progress dialog and clear listview from possible
* previous search.
*
* @inheritDoc
*/
@Override
protected void onCancelled(ArrayAdapter<SqlItem> adapter) {
ListView lv_ = (ListView) activity_.findViewById(R.id.results);
lv_.setAdapter(null);
pg_.dismiss();
}
/**
* Returns information about a specific item.
*
* @param id
* Data identifier.
* @return Item information.
*/
public SqlItem getData(int id) {
return adapter_.getItem(id);
}
}

View File

@ -0,0 +1,205 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import org.json.JSONException;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.github.nutomic.releasetracker.sql.SqlHelper;
import com.github.savvasdalkitsis.jtmdb.GeneralSettings;
import com.github.savvasdalkitsis.jtmdb.Person;
/**
* This class searches on all APIs (one at a time -> pass object that contains
* type and query).
*/
public class ApiSearchPerson extends
AsyncTask<String, Integer, ArrayAdapter<SqlPerson>> {
private static final String TAG = "ApiSearch";
private final Activity activity_;
private ProgressDialog pg_;
private ArrayAdapter<SqlPerson> adapter_;
/**
* Constructor, get activity to display ProgressDialog.
*
* @param activity
* Activity this class is constructed from.
*/
public ApiSearchPerson(Activity activity) {
activity_ = activity;
}
/**
* Creates ProgressDialog before starting web request.
*/
@Override
protected void onPreExecute() {
pg_ = ProgressDialog.show(activity_, "", "Loading. Please wait...",
true, true, new OnCancelListener() {
public void onCancel(DialogInterface di) {
ApiSearchPerson.this.cancel(true);
}
});
pg_.show();
}
/**
* Searches remote API for query, depending on selected type. Must have
* exactly two parameters. May return null (valid adapter value for
* ListView).
*
* @param params
* [0] Search query.
* @param params
* [1] Search type.
*/
@Override
protected ArrayAdapter<SqlPerson> doInBackground(String... params) {
List<SqlPerson> ri = new ArrayList<SqlPerson>();
switch (Integer.parseInt(params[1])) {
case SqlHelper.API_ALL:
break;
case SqlHelper.API_MOVIE:
GeneralSettings.setAPILocale(Locale.getDefault());
GeneralSettings.setApiKey((String) activity_
.getText(R.string.api_key_tmdb));
List<Person> persons;
try {
persons = Person.search(params[0]);
} catch (IOException e) {
Log.w(TAG, "TMDB search failed.");
e.printStackTrace();
return null;
} catch (JSONException e) {
Log.w(TAG, "TMDB json read failed.");
e.printStackTrace();
return null;
}
for (int i = 0; i < persons.size(); i++) {
if (isCancelled())
return null;
Person cur = persons.get(i);
ri.add(new SqlPerson(-1, cur.getName(), SqlHelper.API_MOVIE,
Integer.toString(cur.getID())));
}
break;
}
Collections.sort(ri, new Comparator<SqlPerson>() {
/**
* Compare two persons by name.
*
* @inheritDoc
*/
public int compare(SqlPerson lhs, SqlPerson rhs) {
SqlPerson l = (SqlPerson) lhs;
SqlPerson r = (SqlPerson) rhs;
return l.name.compareToIgnoreCase(r.name);
}
});
return new ArrayAdapter<SqlPerson>(activity_, 0, ri) {
@Override
/**
* Create or initialize view with custom texts
* @inheritDoc
*/
public View getView(int position, View row,
android.view.ViewGroup parent) {
if (row == null) {
row = activity_.getLayoutInflater().inflate(
R.layout.listview_item, null);
}
TextView title = (TextView) row.findViewById(R.id.title);
title.setText(getItem(position).name);
return row;
}
};
}
/**
* Closes ProgressDialog and add all items into ListView.
*
* @param adapter
* Contains search results.
*/
@Override
protected void onPostExecute(ArrayAdapter<SqlPerson> adapter) {
adapter_ = adapter;
ListView lv_ = (ListView) activity_.findViewById(R.id.results);
lv_.setAdapter(adapter_);
pg_.dismiss();
}
/**
* Search cancelled, hide progress dialog and clear listview from possible
* previous search.
*
* @inheritDoc
*/
@Override
protected void onCancelled(ArrayAdapter<SqlPerson> adapter) {
ListView lv_ = (ListView) activity_.findViewById(R.id.results);
lv_.setAdapter(null);
pg_.dismiss();
}
/**
* Returns information about a specific item.
*
* @param id
* Data identifier.
* @return Item information.
*/
public SqlPerson getData(int id) {
return adapter_.getItem(id);
}
}

View File

@ -0,0 +1,303 @@
/**
* Copyright (C) 2011, Karsten Priegnitz
*
* Permission to use, copy, modify, and distribute this piece of software
* for any purpose with or without fee is hereby granted, provided that
* the above copyright notice and this permission notice appear in the
* source code of all copies.
*
* It would be appreciated if you mention the author in your change log,
* contributors list or the like.
*
* @author: Karsten Priegnitz
* @see: http://code.google.com/p/android-change-log/
*/
package com.github.nutomic.releasetracker;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import android.webkit.WebView;
public class ChangeLog {
private final Context context;
private String lastVersion, thisVersion;
// this is the key for storing the version name in SharedPreferences
private static final String VERSION_KEY = "PREFS_VERSION_KEY";
/**
* Constructor
*
* Retrieves the version names and stores the new version name in
* SharedPreferences
*
* @param context
*/
public ChangeLog(Context context) {
this(context, PreferenceManager.getDefaultSharedPreferences(context));
}
/**
* Constructor
*
* Retrieves the version names and stores the new version name in
* SharedPreferences
*
* @param context
* @param sp
* the shared preferences to store the last version name into
*/
public ChangeLog(Context context, SharedPreferences sp) {
this.context = context;
// get version numbers
this.lastVersion = sp.getString(VERSION_KEY, "");
Log.d(TAG, "lastVersion: " + lastVersion);
// Start changed code.
/*
* @see http://code.google.com/p/android-change-log/issues/detail?id=5
* try { this.thisVersion =
* context.getPackageManager().getPackageInfo(context.getPackageName(),
* 0).versionName; } catch (NameNotFoundException e) { this.thisVersion
* = "?"; Log.e(TAG, "could not get version name from manifest!");
* e.printStackTrace(); }
*/
this.thisVersion = context.getString(R.string.app_version_name);
// End changed code.
Log.d(TAG, "appVersion: " + this.thisVersion);
// save new version number to preferences
SharedPreferences.Editor editor = sp.edit();
editor.putString(VERSION_KEY, this.thisVersion);
editor.commit();
}
/**
* @return The version name of the last installation of this app (as
* described in the former manifest). This will be the same as
* returned by <code>getThisVersion()</code> the second time this
* version of the app is launched (more precisely: the second time
* ChangeLog is instantiated).
* @see AndroidManifest.xml#android:versionName
*/
public String getLastVersion() {
return this.lastVersion;
}
/**
* @return The version name of this app as described in the manifest.
* @see AndroidManifest.xml#android:versionName
*/
public String getThisVersion() {
return this.thisVersion;
}
/**
* @return <code>true</code> if this version of your app is started the
* first time
*/
public boolean firstRun() {
return !this.lastVersion.equals(this.thisVersion);
}
/**
* @return <code>true</code> if your app is started the first time ever.
* Also <code>true</code> if your app was deinstalled and installed
* again.
*/
public boolean firstRunEver() {
return "".equals(this.lastVersion);
}
/**
* @return an AlertDialog displaying the changes since the previous
* installed version of your app (what's new).
*/
public AlertDialog getLogDialog() {
return this.getDialog(false);
}
/**
* @return an AlertDialog with a full change log displayed
*/
public AlertDialog getFullLogDialog() {
return this.getDialog(true);
}
private AlertDialog getDialog(boolean full) {
WebView wv = new WebView(this.context);
wv.setBackgroundColor(0); // transparent
// wv.getSettings().setDefaultTextEncodingName("utf-8");
wv.loadDataWithBaseURL(null, this.getLog(full), "text/html", "UTF-8",
null);
AlertDialog.Builder builder = new AlertDialog.Builder(this.context);
builder.setTitle(
context.getResources().getString(
full ? R.string.changelog_full_title
: R.string.changelog_title))
.setView(wv)
.setCancelable(false)
.setPositiveButton(
context.getResources().getString(android.R.string.ok),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
// Begin of changed code.
if (!full) {
builder.setNegativeButton(R.string.changelog_show_full,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
getFullLogDialog().show();
}
});
}
// End of changed code.
return builder.create();
}
/**
* @return HTML displaying the changes since the previous installed version
* of your app (what's new)
*/
public String getLog() {
return this.getLog(false);
}
/**
* @return HTML which displays full change log
*/
public String getFullLog() {
return this.getLog(true);
}
/** modes for HTML-Lists (bullet, numbered) */
private enum Listmode {
NONE, ORDERED, UNORDERED,
};
private Listmode listMode = Listmode.NONE;
private StringBuffer sb = null;
private static final String EOCL = "END_OF_CHANGE_LOG";
private String getLog(boolean full) {
// read changelog.txt file
sb = new StringBuffer();
try {
InputStream ins = context.getResources().openRawResource(
R.raw.changelog);
BufferedReader br = new BufferedReader(new InputStreamReader(ins));
String line = null;
boolean advanceToEOVS = false; // if true: ignore further version
// sections
while ((line = br.readLine()) != null) {
line = line.trim();
char marker = line.length() > 0 ? line.charAt(0) : 0;
if (marker == '$') {
// begin of a version section
this.closeList();
String version = line.substring(1).trim();
// stop output?
if (!full) {
if (this.lastVersion.equals(version)) {
advanceToEOVS = true;
} else if (version.equals(EOCL)) {
advanceToEOVS = false;
}
}
} else if (!advanceToEOVS) {
switch (marker) {
case '%':
// line contains version title
this.closeList();
sb.append("<div class='title'>"
+ line.substring(1).trim() + "</div>\n");
break;
case '_':
// line contains version title
this.closeList();
sb.append("<div class='subtitle'>"
+ line.substring(1).trim() + "</div>\n");
break;
case '!':
// line contains free text
this.closeList();
sb.append("<div class='freetext'>"
+ line.substring(1).trim() + "</div>\n");
break;
case '#':
// line contains numbered list item
this.openList(Listmode.ORDERED);
sb.append("<li>" + line.substring(1).trim() + "</li>\n");
break;
case '*':
// line contains bullet list item
this.openList(Listmode.UNORDERED);
sb.append("<li>" + line.substring(1).trim() + "</li>\n");
break;
default:
// no special character: just use line as is
this.closeList();
sb.append(line + "\n");
}
}
}
this.closeList();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
private void openList(Listmode listMode) {
if (this.listMode != listMode) {
closeList();
if (listMode == Listmode.ORDERED) {
sb.append("<div class='list'><ol>\n");
} else if (listMode == Listmode.UNORDERED) {
sb.append("<div class='list'><ul>\n");
}
this.listMode = listMode;
}
}
private void closeList() {
if (this.listMode == Listmode.ORDERED) {
sb.append("</ol></div>\n");
} else if (this.listMode == Listmode.UNORDERED) {
sb.append("</ul></div>\n");
}
this.listMode = Listmode.NONE;
}
private static final String TAG = "ChangeLog";
/**
* manually set the last version name - for testing purposes only
*
* @param lastVersion
*/
void setLastVersion(String lastVersion) {
this.lastVersion = lastVersion;
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import android.widget.ScrollView;
import android.widget.TextView;
/**
* A random collection of functions.
*/
public class Functions {
private final static String TAG = "Functions";
/**
* Create an overlay dialog that displays the contents of a resource file.
*
* @param activity
* Activity to display the dialog in.
* @param title
* The dialog title.
* @param resource
* d of a raw resource file to be displayed.
*/
public static void overlay(Activity activity, String title, int resource) {
Log.i(TAG, "Displaying overlay.");
String text = "";
try {
BufferedReader buffreader = new BufferedReader(
new InputStreamReader(activity.getResources()
.openRawResource(resource)));
String line;
while ((line = buffreader.readLine()) != null) {
text += line + "\n";
}
} catch (IOException e) {
Log.w(TAG, "Failed to open raw resource file.", e);
return;
}
TextView tv = new TextView(activity);
tv.setText(text.toString());
ScrollView sv = new ScrollView(activity);
sv.addView(tv);
new AlertDialog.Builder(activity).setTitle(title)
.setPositiveButton(android.R.string.ok, null).setView(sv)
.show();
}
/**
* Check for an active internet connection.
*
* @param context
* Application context.
* @return true if a network connection is available.
*/
public static boolean isNetworkAvailable() {
ConnectivityManager connectivityManager = (ConnectivityManager) ReleaseTracker
.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager
.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.os.IBinder;
import com.github.nutomic.releasetracker.sql.ItemTable;
/**
* Regularly checks for items that have been released, and shows a notification
* if any are found.
*/
public class NotificationService extends Service {
@Override
public void onCreate() {
ItemTable.checkReleased();
Cursor cursor = ItemTable.getReleasedTitles();
if (cursor.getCount() != 0) {
String titles = "";
while (!cursor.isLast()) {
titles += cursor.getString(ItemTable.FIELD_TITLE) + " ";
cursor.moveToNext();
}
titles = titles.substring(0, titles.length() - 1);
new Notification.Builder(this)
.setContentTitle(
(cursor.getCount() == 1) ? getString(R.string.notification_released_single)
: getString(R.string.notification_released_single))
.setContentText(titles)
.setTicker(titles)
.setNumber(cursor.getCount())
.setLargeIcon(
BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher))
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(
PendingIntent.getActivity(this, 0, new Intent(
"com.github.nutomic.releasetracker.Home"),
0)).setOngoing(false).setAutoCancel(true);
}
cursor.close();
stopSelf();
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker;
import java.util.concurrent.atomic.AtomicInteger;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import com.github.nutomic.releasetracker.sql.SqlHelper;
/*
* Main app activity, shows a list of all release items.
*
* Test:
* everything on minSdkVersion (7)
* notification service
* update service
* screen rotate in item/person search
* contact does not set to field
*
* general todo:
* select time for notification
* select how many days prior to release notification should be displayed
* show notification (in app?) when new item from person is announced
*
* release:
* http://developer.android.com/guide/publishing/app-signing.html
* http://developer.android.com/guide/developing/tools/proguard.html
* ads/selling: make new bank account, new post address
* https://github.com/sonyericssondev/ApkAnalyser/wiki
*/
public class ReleaseTracker extends Application implements
Application.ActivityLifecycleCallbacks {
private static AtomicInteger activityCount_ = new AtomicInteger(0);
private static Context context_;
@SuppressLint("NewApi")
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= 9) {
registerActivityLifecycleCallbacks(this);
}
context_ = getApplicationContext();
}
/**
* Returns global application context for objects that are active longer
* than the activity that started them or that do not need activity
* information.
*
* @return Global application context.
*/
public static Context getContext() {
return context_;
}
/**
* Closes SQL connection if no activities are open.
*/
public void onActivityStopped(Activity activity) {
if (activityCount_.decrementAndGet() < 1) {
SqlHelper.deleteInstance();
}
}
/**
* Close SQL connection on low memory.
*/
@Override
public void onLowMemory() {
SqlHelper.deleteInstance();
}
public void onActivityStarted(Activity activity) {
}
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
public void onActivityDestroyed(Activity activity) {
}
public void onActivityPaused(Activity activity) {
}
public void onActivityResumed(Activity activity) {
}
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker;
import java.util.Date;
import com.github.nutomic.releasetracker.sql.ItemTable;
/**
* Data container for row from {@link ItemTable}.
*/
public class SqlItem {
public final int id;
public final String title;
public final String artist;
public final Date releaseDate;
public final String apiId;
public final int apiType;
public final boolean notified;
public final boolean released;
public SqlItem(int pId, String pTitle, String pArtist, Date date,
String pApiId, int pApiType, boolean pNotified, boolean pReleased) {
id = pId;
title = pTitle;
artist = pArtist;
releaseDate = date;
apiId = pApiId;
apiType = pApiType;
notified = pNotified;
released = pReleased;
}
/**
* Leaves ID empty.
*/
public SqlItem(String pTitle, String pArtist, Date date, String pApiId,
int pApiType, boolean pNotified, boolean pReleased) {
this(-1, pTitle, pArtist, date, pApiId, pApiType, pNotified, pReleased);
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker;
import com.github.nutomic.releasetracker.sql.PersonTable;
/**
* Data container for row from {@link PersonTable}.
*/
public class SqlPerson {
public final int id;
public final String name;
public final int apiType;
public final String apiId;
public SqlPerson(int pId, String pName, int pApiType, String pApiId) {
id = pId;
name = pName;
apiType = pApiType;
apiId = pApiId;
}
/**
* Leaves ID empty.
*/
public SqlPerson(String pName, int pApiType, String pApiId) {
this(-1, pName, pApiType, pApiId);
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker;
import java.util.ArrayList;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TabHost;
import android.widget.TabWidget;
/**
* Handles tabbed fragment activity management. This is a helper class that
* implements the management of tabs and all details of connecting a ViewPager
* with associated TabHost. It relies on a trick. Normally a tab host has a
* simple API for supplying a View or Intent that each tab will show. This is
* not sufficient for switching between pages. So instead we make the content
* part of the tab host 0dp high (it is not shown) and the TabsAdapter supplies
* its own dummy view to show as the tab content. It listens to changes in tabs,
* and takes care of switch to the correct paged in the ViewPager whenever the
* selected tab changes.
*
* from: https://github.com/JakeWharton/ActionBarSherlock/blob/master/samples/
* fragments/src/com/actionbarsherlock/sample/fragments/FragmentTabsPager.java
*/
public class TabsAdapter extends FragmentPagerAdapter implements
TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
private final Context mContext;
private final TabHost mTabHost;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
static final class TabInfo {
// private final String tag;
private final Class<?> clss;
private final Bundle args;
TabInfo(String _tag, Class<?> _class, Bundle _args) {
// tag = _tag;
clss = _class;
args = _args;
}
}
static class DummyTabFactory implements TabHost.TabContentFactory {
private final Context mContext;
public DummyTabFactory(Context context) {
mContext = context;
}
public View createTabContent(String tag) {
View v = new View(mContext);
v.setMinimumWidth(0);
v.setMinimumHeight(0);
return v;
}
}
public TabsAdapter(FragmentActivity activity, TabHost tabHost,
ViewPager pager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mTabHost = tabHost;
mViewPager = pager;
mTabHost.setOnTabChangedListener(this);
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
tabSpec.setContent(new DummyTabFactory(mContext));
String tag = tabSpec.getTag();
TabInfo info = new TabInfo(tag, clss, args);
mTabs.add(info);
mTabHost.addTab(tabSpec);
notifyDataSetChanged();
}
@Override
public int getCount() {
return mTabs.size();
}
@Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
return Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
public void onTabChanged(String tabId) {
int position = mTabHost.getCurrentTab();
mViewPager.setCurrentItem(position);
}
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
public void onPageSelected(int position) {
// Unfortunately when TabHost changes the current tab, it kindly
// also takes care of putting focus on it when not in touch mode.
// The jerk.
// This hack tries to prevent this from pulling focus out of our
// ViewPager.
TabWidget widget = mTabHost.getTabWidget();
int oldFocusability = widget.getDescendantFocusability();
widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mTabHost.setCurrentTab(position);
widget.setDescendantFocusability(oldFocusability);
}
public void onPageScrollStateChanged(int state) {
}
}

View File

@ -0,0 +1,175 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker;
import java.io.IOException;
import java.util.Date;
import java.util.Locale;
import org.json.JSONException;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
import com.github.nutomic.releasetracker.sql.ItemTable;
import com.github.nutomic.releasetracker.sql.PersonTable;
import com.github.nutomic.releasetracker.sql.SqlHelper;
import com.github.savvasdalkitsis.jtmdb.FilmographyInfo;
import com.github.savvasdalkitsis.jtmdb.GeneralSettings;
import com.github.savvasdalkitsis.jtmdb.Movie;
import com.github.savvasdalkitsis.jtmdb.Person;
/**
* Checks for newly announced items by artists and for updated item release
* dates.
*/
public class UpdateCheckService extends Service {
private final static String TAG = "UpdateCheckService";
/**
* Runs data update if more days than specified in setting have passed. Days
* where this service is not launched are not counted.
*/
@Override
public void onCreate() {
int SECONDS_PER_DAY = 86400;
SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(this);
int interval = Integer.getInteger(preferences.getString(
"automatic_data_updates", "2"));
long elapsedDays = (System.currentTimeMillis() - preferences.getInt(
"last_data_update_timestamp", 0)) / SECONDS_PER_DAY;
if ((interval != -1)
&& (elapsedDays >= interval)
&& isWifiAvailable()
|| (Functions.isNetworkAvailable() && !preferences.getBoolean(
"automatic_data_updates_wifi_only", true))) {
new Thread(run).run();
Editor editor = preferences.edit();
editor.putLong("last_data_update_timestamp",
System.currentTimeMillis());
editor.commit();
}
stopSelf();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
public final static Runnable run = new Runnable() {
public void run() {
GeneralSettings.setAPILocale(Locale.getDefault());
GeneralSettings.setApiKey((String) ReleaseTracker.getContext()
.getText(R.string.api_key_tmdb));
// Check for updated movies.
Cursor cursor = ItemTable.getDataCursor(false);
cursor.moveToFirst();
while (cursor.isAfterLast()) {
switch (cursor.getInt(PersonTable.FIELD_API_ID)) {
case SqlHelper.API_MOVIE:
Movie movie = null;
try {
movie = Movie.getInfo(cursor
.getInt(ItemTable.FIELD_API_ID));
} catch (IOException e) {
Log.w(TAG, "TMDB access failed.", e);
return;
} catch (JSONException e) {
Log.w(TAG, "TMDB json read failed.", e);
return;
}
ItemTable.updateRow(cursor.getInt(ItemTable.FIELD_ID),
movie.getName(), movie.getReleasedDate());
break;
}
cursor.moveToNext();
}
// Check for newly announced items by persons.
cursor = PersonTable.getDataCursor();
cursor.moveToFirst();
Date now = new Date();
while (cursor.isAfterLast()) {
switch (cursor.getInt(PersonTable.FIELD_API_ID)) {
case SqlHelper.API_MOVIE:
Person person = null;
try {
person = Person.getInfo(cursor
.getInt(PersonTable.FIELD_API_ID));
} catch (IOException e) {
Log.w(TAG, "TMDB access failed.", e);
return;
} catch (JSONException e) {
Log.w(TAG, "TMDB json read failed.", e);
return;
}
FilmographyInfo[] films = (FilmographyInfo[]) person
.getFilmography().toArray();
for (int i = 0; i < films.length; i++) {
if (now.before(films[i].getReleasedDate())
&& ItemTable.itemExists(SqlHelper.API_MOVIE,
Integer.toString(films[i].getID()))) {
ItemTable.insertRow(new SqlItem(films[i].getName(),
person.getName(), films[i]
.getReleasedDate(), Integer
.toString(films[i].getID()),
SqlHelper.API_MOVIE, false, false));
}
}
break;
}
cursor.moveToNext();
}
}
};
public void get() {
};
private static boolean isWifiAvailable() {
ConnectivityManager connectivityManager = (ConnectivityManager) ReleaseTracker
.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager
.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected()
&& activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI;
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker.activities;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.widget.TabHost;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.github.nutomic.releasetracker.R;
import com.github.nutomic.releasetracker.TabsAdapter;
import com.github.nutomic.releasetracker.fragments.AddCustomItem;
import com.github.nutomic.releasetracker.fragments.Search;
/**
* Tabbed Activity to add an item or person to the SQL database, either through
* API or by direct input.
*/
public class AddItem extends SherlockFragmentActivity {
TabHost tabHost_;
ViewPager viewPager_;
TabsAdapter tabsAdapter_;
/**
* Creates tabs.
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.add_item);
tabHost_ = (TabHost) findViewById(android.R.id.tabhost);
tabHost_.setup();
viewPager_ = (ViewPager) findViewById(R.id.pager);
tabsAdapter_ = new TabsAdapter(this, tabHost_, viewPager_);
tabsAdapter_
.addTab(tabHost_.newTabSpec("search").setIndicator(
getString(R.string.additem_search)), Search.class, null);
tabsAdapter_.addTab(
tabHost_.newTabSpec("custom").setIndicator(
getString(R.string.additem_custom)),
AddCustomItem.class, null);
}
/**
* Restores tab that was last opened in this activity, no matter if that was
* for items or persons.
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
tabHost_.setCurrentTabByTag(savedInstanceState.getString("tab"));
super.onRestoreInstanceState(savedInstanceState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("tab", tabHost_.getCurrentTabTag());
super.onSaveInstanceState(outState);
}
}

View File

@ -0,0 +1,180 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker.activities;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import com.actionbarsherlock.app.SherlockActivity;
import com.github.nutomic.releasetracker.ApiSearchPerson;
import com.github.nutomic.releasetracker.Functions;
import com.github.nutomic.releasetracker.R;
import com.github.nutomic.releasetracker.sql.PersonTable;
import com.github.nutomic.releasetracker.sql.SqlHelper;
/**
* Search APIs for a person and add to SQL database.
*/
public class AddPerson extends SherlockActivity implements OnItemClickListener,
OnKeyListener, OnClickListener {
private EditText et_;
private ListView lv_;
private ApiSearchPerson api_;
private boolean searching_ = false;
class Container {
public ApiSearchPerson api;
public ListView lv;
public boolean searching;
}
/**
* Sets this class as OnClickListener for the search button and the results,
* handles actions in EditText.
*
* @inheritDoc
*/
@SuppressWarnings("deprecation")
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search);
Container c = (Container) getLastNonConfigurationInstance();
if (c != null) {
api_ = c.api;
lv_ = c.lv;
searching_ = c.searching;
} else {
lv_ = (ListView) findViewById(R.id.results);
lv_.setOnItemClickListener(this);
lv_.setEmptyView(findViewById(R.id.empty));
}
et_ = (EditText) findViewById(R.id.text);
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
et_.setOnKeyListener(this);
Button b = (Button) findViewById(R.id.button);
b.setOnClickListener(this);
}
@Override
public Object onRetainNonConfigurationInstance() {
Container c = new Container();
c.api = api_;
c.lv = lv_;
c.searching = searching_;
return c;
}
/**
* Enter key pressed in search text.
*
* @inheritDoc
*/
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (v.equals(et_)
&& ((keyCode == KeyEvent.KEYCODE_ENTER) || (keyCode == KeyEvent.KEYCODE_DPAD_CENTER))
&& (event.getAction() == KeyEvent.ACTION_DOWN)) {
onClick(null);
return true;
}
return false;
}
/**
* Searches api with user text.
*/
public void onClick(View v) {
if (!searching_ && (et_.getText().length() > 0)) {
if (!Functions.isNetworkAvailable()) {
new AlertDialog.Builder(this)
.setMessage(R.string.error_require_internet)
.setPositiveButton(android.R.string.ok, null)
.show();
} else {
searching_ = true;
api_ = new ApiSearchPerson(this);
api_.execute(et_.getText().toString(),
Integer.toString(SqlHelper.API_MOVIE));
searching_ = false;
}
}
}
/**
* Adds the item that was selected in the ListView to the database.
*
* @inheritDoc
*/
public void onItemClick(AdapterView<?> lv, final View v, int position,
final long id) {
DialogInterface.OnClickListener onPositiveClick = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
PersonTable.insertRow(api_.getData((int) id));
}
};
new AlertDialog.Builder(this).setMessage(R.string.search_add_item)
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes, onPositiveClick)
.show();
new AlertDialog.Builder(this)
.setMessage(getString(R.string.additem_added))
.setPositiveButton(android.R.string.ok, null).show();
}
/**
* Hardware search button pressed, focus search text.
*
* @inheritDoc
*/
@Override
public boolean onSearchRequested() {
et_.requestFocus();
return false;
}
}

View File

@ -0,0 +1,121 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker.activities;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ResourceCursorAdapter;
import com.actionbarsherlock.app.SherlockActivity;
import com.github.nutomic.releasetracker.R;
import com.github.nutomic.releasetracker.sql.ItemTable;
import com.github.nutomic.releasetracker.sql.SqlCursorAdapter;
public class History extends SherlockActivity {
ResourceCursorAdapter adapter_;
/**
* Initializes views.
*
* @inheritDoc
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.history);
adapter_ = new SqlCursorAdapter(this, R.layout.listview_item, null,
false);
ListView lv = (ListView) findViewById(R.id.list);
registerForContextMenu(lv);
lv.setAdapter(adapter_);
lv.setEmptyView(findViewById(R.id.empty));
lv.setStackFromBottom(true);
}
/**
* Get a new cursor in case the old one was invalidated.
*/
@Override
public void onStart() {
super.onStart();
adapter_.changeCursor(ItemTable.getDataCursor(true));
}
/**
* Create ListView item context menu.
*
* @inheritDoc
*/
@Override
public void onCreateContextMenu(ContextMenu menu, final View v,
final ContextMenu.ContextMenuInfo menuInfo) {
final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
OnMenuItemClickListener listener = new OnMenuItemClickListener() {
public boolean onMenuItemClick(android.view.MenuItem item) {
switch (item.getItemId()) {
case 0:
new AlertDialog.Builder(History.this)
.setMessage(R.string.home_delete_item)
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(
DialogInterface dialog,
int which) {
ItemTable.deleteRow(info.id);
adapter_.changeCursor(ItemTable
.getDataCursor(true));
}
}).show();
return true;
}
return false;
}
};
menu.setHeaderTitle(adapter_.getCursor().getString(
ItemTable.FIELD_TITLE));
menu.add(getString(R.string.contextmenu_delete))
.setOnMenuItemClickListener(listener);
menu.add(getString(R.string.contextmenu_link_open)).setIntent(
new Intent(Intent.ACTION_VIEW, Uri
.parse("http://www.themoviedb.org/movie/"
+ adapter_.getCursor().getString(
ItemTable.FIELD_API_ID))));
}
}

View File

@ -0,0 +1,200 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker.activities;
import java.util.Locale;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ResourceCursorAdapter;
import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.view.Menu;
import com.github.nutomic.releasetracker.ChangeLog;
import com.github.nutomic.releasetracker.Functions;
import com.github.nutomic.releasetracker.NotificationService;
import com.github.nutomic.releasetracker.R;
import com.github.nutomic.releasetracker.UpdateCheckService;
import com.github.nutomic.releasetracker.fragments.Search;
import com.github.nutomic.releasetracker.sql.ItemTable;
import com.github.nutomic.releasetracker.sql.SqlCursorAdapter;
/**
* Displays a list of release items.
*/
public class Home extends SherlockActivity {
ResourceCursorAdapter adapter_;
private ListView lv_;
/**
* Initialize views, display changelog if needed.
*
* @inheritDoc
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home);
// this will slow down loading time on big db
ItemTable.checkReleased();
adapter_ = new SqlCursorAdapter(this, R.layout.listview_item, null,
true);
lv_ = (ListView) findViewById(R.id.list);
registerForContextMenu(lv_);
lv_.setItemsCanFocus(true);
lv_.setEmptyView(findViewById(R.id.empty));
lv_.setAdapter(adapter_);
ChangeLog cl = new ChangeLog(this);
if (cl.firstRunEver()) {
Functions.overlay(this, getString(R.string.home_welcome),
R.raw.first_start);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.setInexactRepeating(AlarmManager.RTC, 0,
AlarmManager.INTERVAL_DAY, PendingIntent.getBroadcast(this,
0, new Intent(this, NotificationService.class), 0));
am.setInexactRepeating(AlarmManager.RTC, 0,
AlarmManager.INTERVAL_DAY, PendingIntent.getBroadcast(this,
0, new Intent(this, UpdateCheckService.class), 0));
} else if (cl.firstRun()) {
cl.getLogDialog().show();
}
}
/**
* CheckBox on a released item clicked, set user_notified to true and remove
* item from ListView.
*
* @inheritDoc
*/
public void checkBoxClick(View v) {
ItemTable.setNotified(Long.parseLong((String) v.getTag()));
adapter_.changeCursor(ItemTable.getDataCursor(false));
}
/**
* Get a new cursor in case the old one was invalidated.
*/
@Override
public void onStart() {
super.onStart();
adapter_.changeCursor(ItemTable.getDataCursor(false));
}
/**
* Initialize action bar with items and set intent for each menu item.
*
* @inheritDoc
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.actionbar_home, menu);
menu.findItem(R.id.add_item).setIntent(new Intent(this, AddItem.class));
menu.findItem(R.id.history).setIntent(new Intent(this, History.class));
menu.findItem(R.id.persons).setIntent(new Intent(this, Persons.class));
menu.findItem(R.id.preferences).setIntent(
new Intent(this, Preferences.class));
return true;
}
/**
* Create ListView item context menu.
*
* @inheritDoc
*/
@Override
public void onCreateContextMenu(ContextMenu menu, final View v,
final ContextMenu.ContextMenuInfo menuInfo) {
final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
OnMenuItemClickListener listener = new OnMenuItemClickListener() {
public boolean onMenuItemClick(android.view.MenuItem item) {
switch (item.getItemId()) {
case 0:
new AlertDialog.Builder(Home.this)
.setMessage(R.string.home_delete_item)
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(
DialogInterface dialog,
int which) {
ItemTable.deleteRow(info.id);
adapter_.changeCursor(ItemTable
.getDataCursor(false));
}
}).show();
return true;
}
return false;
}
};
menu.setHeaderTitle(adapter_.getCursor().getString(
ItemTable.FIELD_TITLE));
menu.add(getString(R.string.contextmenu_delete))
.setOnMenuItemClickListener(listener);
menu.add(getString(R.string.contextmenu_link_open)).setIntent(
new Intent(Intent.ACTION_VIEW, Uri
.parse(getString(R.string.api_tmdb_url_movie)
+ adapter_.getCursor().getString(
ItemTable.FIELD_API_ID)
+ getString(R.string.api_tmdb_param_language)
+ Locale.getDefault().getLanguage())));
}
/**
* Hardware search button pressed, switch to Search activity.
*
* @inheritDoc
*/
@Override
public boolean onSearchRequested() {
startActivity(new Intent(this, Search.class));
return false;
}
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker.activities;
import java.util.Locale;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ResourceCursorAdapter;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.view.Menu;
import com.github.nutomic.releasetracker.R;
import com.github.nutomic.releasetracker.sql.ItemTable;
import com.github.nutomic.releasetracker.sql.PersonTable;
public class Persons extends SherlockActivity {
ResourceCursorAdapter adapter_;
private ListView lv_;
/**
* Initialize views, display changelog if needed.
*
* @inheritDoc
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home);
adapter_ = new ResourceCursorAdapter(this, R.layout.listview_item,
null, 0) {
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView titleView = (TextView) view.findViewById(R.id.title);
titleView.setText(PersonTable.FIELD_NAME);
}
};
lv_ = (ListView) findViewById(R.id.list);
registerForContextMenu(lv_);
lv_.setItemsCanFocus(true);
lv_.setEmptyView(findViewById(R.id.empty));
lv_.setAdapter(adapter_);
}
/**
* Get a new cursor in case the old one was invalidated.
*/
@Override
public void onStart() {
super.onStart();
adapter_.changeCursor(PersonTable.getDataCursor());
}
/**
* Initialize action bar with items and set intent for each menu item.
*
* @inheritDoc
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.actionbar_persons, menu);
menu.findItem(R.id.add_item).setIntent(
new Intent(this, AddPerson.class));
return true;
}
/**
* Create ListView item context menu.
*
* @inheritDoc
*/
@Override
public void onCreateContextMenu(ContextMenu menu, final View v,
final ContextMenu.ContextMenuInfo menuInfo) {
final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
OnMenuItemClickListener listener = new OnMenuItemClickListener() {
public boolean onMenuItemClick(android.view.MenuItem item) {
switch (item.getItemId()) {
case 0:
new AlertDialog.Builder(Persons.this)
.setMessage(R.string.home_delete_item)
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(
DialogInterface dialog,
int which) {
PersonTable.deleteRow(info.id);
adapter_.changeCursor(PersonTable
.getDataCursor());
}
}).show();
return true;
}
return false;
}
};
menu.setHeaderTitle(adapter_.getCursor().getString(
ItemTable.FIELD_TITLE));
menu.add(getString(R.string.contextmenu_delete))
.setOnMenuItemClickListener(listener);
menu.add(getString(R.string.contextmenu_link_open)).setIntent(
new Intent(Intent.ACTION_VIEW, Uri
.parse(getString(R.string.api_tmdb_url_person)
+ adapter_.getCursor().getString(
PersonTable.FIELD_API_ID)
+ getString(R.string.api_tmdb_param_language)
+ Locale.getDefault().getLanguage())));
}
}

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker.activities;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.util.Log;
import com.actionbarsherlock.app.SherlockPreferenceActivity;
import com.github.nutomic.releasetracker.ChangeLog;
import com.github.nutomic.releasetracker.Functions;
import com.github.nutomic.releasetracker.R;
import com.github.nutomic.releasetracker.UpdateCheckService;
public class Preferences extends SherlockPreferenceActivity implements
OnPreferenceClickListener, OnPreferenceChangeListener {
private static final String TAG = "Preferences";
/**
* Set listeners for settings clicks.
*
* @inheritDoc
*/
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
findPreference("changelog_view").setOnPreferenceClickListener(this);
findPreference("license_android_change_log")
.setOnPreferenceClickListener(this);
findPreference("license_tmdb").setOnPreferenceClickListener(this);
findPreference("license_jtmdb").setOnPreferenceClickListener(this);
findPreference("automatic_data_updates").setOnPreferenceChangeListener(
this);
}
/**
* User pressed an item with a special action.
*
* @inheritDoc
*/
public boolean onPreferenceClick(Preference preference) {
if (preference.getKey().equals("changelog_view")) {
new ChangeLog(this).getFullLogDialog().show();
} else if (preference.getKey().equals("license_android_change_log")) {
Functions.overlay(this,
getString(R.string.preferences_license_android_change_log),
R.raw.license_lgpl);
} else if (preference.getKey().equals("license_tmdb")) {
Functions.overlay(this,
getString(R.string.preferences_license_tmdb),
R.raw.license_tmdb);
} else if (preference.getKey().equals("license_jtmdb")) {
Functions.overlay(this,
getString(R.string.preferences_license_jtmdb),
R.raw.license_lgpl);
} else {
return false;
}
return true;
}
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference.getKey() == "automatic_data_updates") {
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
if (Integer.getInteger((String) newValue) != 0) {
Log.i(TAG, "enabled update service");
am.setInexactRepeating(AlarmManager.RTC, 0,
AlarmManager.INTERVAL_DAY, PendingIntent.getBroadcast(
this, 0, new Intent(this,
UpdateCheckService.class), 0));
} else {
Log.i(TAG, "disabled update service");
am.cancel(PendingIntent.getBroadcast(this, 0, new Intent(this,
UpdateCheckService.class), 0));
}
}
return true;
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker.fragments;
import java.util.Calendar;
import java.util.Date;
import android.app.AlertDialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import com.actionbarsherlock.app.SherlockFragment;
import com.github.nutomic.releasetracker.R;
import com.github.nutomic.releasetracker.SqlItem;
import com.github.nutomic.releasetracker.activities.AddItem;
import com.github.nutomic.releasetracker.sql.ItemTable;
import com.github.nutomic.releasetracker.sql.SqlHelper;
/**
* Add an item or person to the SQL database via direct input.
*/
public class AddCustomItem extends SherlockFragment implements OnClickListener {
private AddItem activity_;
private EditText title_;
private EditText artist_;
private DatePicker date_;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.add_custom_item, container, false);
activity_ = (AddItem) getActivity();
title_ = (EditText) v.findViewById(R.id.title);
artist_ = (EditText) v.findViewById(R.id.artist);
date_ = (DatePicker) v.findViewById(R.id.date);
Button b = (Button) v.findViewById(R.id.submit);
b.setOnClickListener(this);
return v;
}
public void onClick(View v) {
if (title_.getText().toString().trim().isEmpty()) {
new AlertDialog.Builder(activity_)
.setMessage(getString(R.string.addcustom_missing_title))
.setPositiveButton(android.R.string.ok, null).show();
return;
}
Calendar calendar = Calendar.getInstance();
calendar.set(date_.getYear(), date_.getMonth(), date_.getDayOfMonth());
ItemTable.insertRow(new SqlItem(-1, title_.getText().toString(),
artist_.getText().toString(), new Date(calendar
.getTimeInMillis()), "", SqlHelper.API_CUSTOM, false,
false));
title_.setText("");
artist_.setText("");
new AlertDialog.Builder(activity_)
.setMessage(getString(R.string.additem_added))
.setPositiveButton(android.R.string.ok, null).show();
}
}

View File

@ -0,0 +1,163 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker.fragments;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import com.actionbarsherlock.app.SherlockFragment;
import com.github.nutomic.releasetracker.ApiSearch;
import com.github.nutomic.releasetracker.Functions;
import com.github.nutomic.releasetracker.R;
import com.github.nutomic.releasetracker.SqlItem;
import com.github.nutomic.releasetracker.sql.ItemTable;
import com.github.nutomic.releasetracker.sql.SqlHelper;
/**
* Search for an item or person and add it to the database.
*/
public class Search extends SherlockFragment implements OnItemClickListener,
OnKeyListener, OnClickListener {
private Activity activity_;
private EditText et_;
private ListView lv_;
private ApiSearch api_;
private boolean searching_ = false;
/**
* Sets this class as OnClickListener for the search button and the results,
* handles actions in EditText.
*
* @inheritDoc
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.search, container, false);
activity_ = getActivity();
lv_ = (ListView) v.findViewById(R.id.results);
lv_.setOnItemClickListener(this);
lv_.setEmptyView(v.findViewById(R.id.empty));
et_ = (EditText) v.findViewById(R.id.text);
activity_.getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
Button b = (Button) v.findViewById(R.id.button);
b.setOnClickListener(this);
container.setOnKeyListener(this);
et_.setOnKeyListener(this);
setRetainInstance(true);
return v;
}
/**
* Enter key pressed in search text.
*
* @inheritDoc
*/
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (v.equals(et_)
&& ((keyCode == KeyEvent.KEYCODE_ENTER) || (keyCode == KeyEvent.KEYCODE_DPAD_CENTER))
&& (event.getAction() == KeyEvent.ACTION_DOWN)) {
onClick(null);
return true;
}
if (keyCode == KeyEvent.KEYCODE_SEARCH) {
activity_.findViewById(R.id.text).requestFocus();
return false;
}
return false;
}
/**
* Searches api with user text.
*/
public void onClick(View v) {
if (!searching_ && (et_.getText().length() > 0)) {
if (!Functions.isNetworkAvailable()) {
new AlertDialog.Builder(activity_)
.setMessage(R.string.error_require_internet)
.setPositiveButton(android.R.string.ok, null)
.show();
} else {
searching_ = true;
api_ = new ApiSearch(activity_);
api_.execute(et_.getText().toString(),
Integer.toString(SqlHelper.API_MOVIE));
searching_ = false;
}
}
}
/**
* Adds the item that was selected in the ListView to the database.
*
* @inheritDoc
*/
public void onItemClick(AdapterView<?> lv, final View v, int position,
final long id) {
DialogInterface.OnClickListener onMessagePositive = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
ItemTable.insertRow((SqlItem) api_.getData((int) id));
new AlertDialog.Builder(activity_)
.setMessage(getString(R.string.additem_added))
.setPositiveButton(android.R.string.ok, null).show();
}
};
new AlertDialog.Builder(activity_).setMessage(R.string.search_add_item)
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes, onMessagePositive)
.show();
}
}

View File

@ -0,0 +1,208 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker.sql;
import java.util.Date;
import android.content.ContentValues;
import android.database.Cursor;
import android.util.Log;
import com.github.nutomic.releasetracker.SqlItem;
/**
* A collection of functions to access the "items" SQL table.
*
* Date is saved as yyyy-mm-dd. The meaning of api_id depends on type (movie ->
* TMDB etc).
*/
public class ItemTable {
private static final String TAG = "ItemTable";
/**
* Indexes for each column
*/
public static final int FIELD_ID = 0;
public static final int FIELD_TITLE = 1;
public static final int FIELD_ARTIST = 2;
public static final int FIELD_RELEASE_DATE = 3;
public static final int FIELD_API_ID = 4;
public static final int FIELD_TYPE = 5;
public static final int FIELD_NOTIFIED = 6;
public static final int FIELD_RELEASED = 7;
private static final String TABLE_NAME = "items";
private ItemTable() {
}
/**
* Returns the string that creates this table.
*
* @return String used to create this table.
*/
public static String getCreateTableString() {
return "CREATE TABLE " + TABLE_NAME + " ("
+ "_id INTEGER PRIMARY KEY, " + "title TEXT, "
+ "artist TEXT, " + "release_date TEXT, " + "api_id TEXT, "
+ "type INTEGER, " + "notified INTEGER, "
+ "released INTEGER);";
}
/**
* Returns a cursor to the titles of all items marked as released by
* {@link #checkReleased()}, but not marked as notified by
* {@link #setNotified(long)}.
*
* @return Result cursor.
*/
public static Cursor getReleasedTitles() {
return SqlHelper
.getInstance()
.getReadableDatabase()
.query(TABLE_NAME, new String[] { "title" },
"released = 1 AND notified = 0", null, null, null, null);
}
/**
* Delete a single row from the database.
*
* @param Id
* Id of the entry to be deleted.
*/
public static void deleteRow(long id) {
Log.i(TAG, "Deleting item with id " + Long.toString(id));
SqlHelper
.getInstance()
.getWritableDatabase()
.delete(TABLE_NAME, "_id = ?",
new String[] { Long.toString(id) });
}
/**
* Saves to database that the user has been informed about the release of a
* specific item.
*
* @param id
* SQL id of the item to be updated.
*/
public static void setNotified(long id) {
Log.d(TAG, "Deleting item with id " + Long.toString(id));
ContentValues values = new ContentValues();
values.put("notified", "1");
SqlHelper
.getInstance()
.getWritableDatabase()
.update(TABLE_NAME, values, "_id = ?",
new String[] { Long.toString(id) });
}
/**
* Checks if any item in the database has been released since the last
* check. Use {@link #itemsReleased()} to get the number of released items.
*/
public static void checkReleased() {
ContentValues values = new ContentValues();
values.put("released", "1");
SqlHelper
.getInstance()
.getWritableDatabase()
.update(TABLE_NAME, values,
"released = 0 AND release_date <= date('now')", null);
}
/**
* Select all unreleased items from database, ordered by date (soonest
* first).
*
* @param history
* If true select items for history (notified is true)
* @return Cursor to results.
*/
public static Cursor getDataCursor(boolean history) {
Cursor result = SqlHelper
.getInstance()
.getReadableDatabase()
.query(TABLE_NAME,
new String[] { "_id", "title", "artist",
"release_date", "api_id", "type", "notified",
"released" }, "notified = ?",
new String[] { (history) ? "1" : "0" }, null, null,
"RELEASE_DATE ASC", null);
return result;
}
/**
* Add a row to the database.
*
* @param data
* SingleItem object containing values.
*/
public static void insertRow(SqlItem data) {
ContentValues values = new ContentValues();
values.put("title", data.title.trim());
values.put("artist", data.artist.trim());
values.put("release_date", (String) dateToSql(data.releaseDate));
values.put("api_id", data.apiId);
values.put("type", data.apiType);
values.put("notified", 0);
values.put("released", (data.releaseDate.after(new Date())) ? 0 : 1);
SqlHelper.getInstance().getWritableDatabase()
.insert(TABLE_NAME, null, values);
}
public static void updateRow(int id, String title, Date date) {
ContentValues values = new ContentValues();
values.put("title", title.trim());
values.put("release_date", dateToSql(date));
values.put("released", (date.after(new Date())) ? 0 : 1);
SqlHelper
.getInstance()
.getReadableDatabase()
.update(TABLE_NAME, values, "_id = ?",
new String[] { Integer.toString(id) });
}
public static boolean itemExists(int apiType, String apiId) {
return SqlHelper
.getInstance()
.getReadableDatabase()
.query(TABLE_NAME, new String[] { "_id" },
"type = ? AND api_id = ?",
new String[] { Integer.toString(apiType), apiId },
null, null, null, null).getCount() > 0;
}
private static String dateToSql(Date date) {
return (String) android.text.format.DateFormat.format("yyyy-MM-dd",
date);
}
}

View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker.sql;
import android.content.ContentValues;
import android.database.Cursor;
import com.github.nutomic.releasetracker.SqlPerson;
/**
* A collection of functions to access the "persons" SQL table.
*
* The meaning of api_id depends on type (movie -> TMDB etc).
*/
public class PersonTable {
/**
* Indexes for each column
*/
public static final int FIELD_ID = 0;
public static final int FIELD_NAME = 1;
public static final int FIELD_API_ID = 2;
public static final int FIELD_TYPE = 3;
private static final String TABLE_NAME = "persons";
private PersonTable() {
}
/**
* Returns the string that creates this table.
*
* @return String used to create this table.
*/
public static String getCreateTableString() {
return "CREATE TABLE " + TABLE_NAME + " (" + "_id INTEGER PRIMARY KEY,"
+ "name TEXT, " + "api_id TEXT" + "type INTEGER);";
}
/**
* Delete a single row from the database.
*
* @param id
* Id of the entry to be deleted.
*/
public static void deleteRow(long id) {
SqlHelper
.getInstance()
.getWritableDatabase()
.delete(TABLE_NAME, "_id = ?",
new String[] { Long.toString(id) });
}
/**
* Select all artists, sorted A to Z.
*
* @return Cursor to results.
*/
public static Cursor getDataCursor() {
Cursor result = SqlHelper
.getInstance()
.getReadableDatabase()
.query(TABLE_NAME, new String[] { "_id", "name" }, null, null,
null, null, "NAME ASC", null);
return result;
}
/**
* Add a row to the database.
*
* @param data
* SingleItem object containing values.
*/
public static void insertRow(SqlPerson data) {
ContentValues values = new ContentValues();
values.put("name", data.name.trim());
values.put("api_id", data.apiId);
values.put("type", data.apiType);
SqlHelper.getInstance().getWritableDatabase()
.insert(TABLE_NAME, null, values);
}
public static void updateRow(int id, String name) {
ContentValues values = new ContentValues();
values.put("name", name.trim());
SqlHelper
.getInstance()
.getReadableDatabase()
.update(TABLE_NAME, values, "_id = ?",
new String[] { Integer.toString(id) });
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker.sql;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ResourceCursorAdapter;
import android.widget.TextView;
import com.github.nutomic.releasetracker.R;
import com.github.nutomic.releasetracker.ReleaseTracker;
/**
* Manages connection between SQL cursor and ListView, disables row highlighting
* and sets custom fields.
*/
public class SqlCursorAdapter extends ResourceCursorAdapter {
private static String TAG = "SqlCursorAdapter";
private boolean enableCheckBox_;
/**
* @inheritDoc
*
* @param context @inheritDoc
* @param layout @inheritDoc
* @param c @inheritDoc
* @param enableCheckBox
* Display a checkbox on items if their release date is in the
* past.
*/
public SqlCursorAdapter(Context context, int layout, Cursor cursor,
boolean enableCheckBox) {
super(context, layout, cursor, false);
enableCheckBox_ = enableCheckBox;
}
/**
* @inheritDoc
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
Date date = sqlToDate(cursor.getString(ItemTable.FIELD_RELEASE_DATE));
initializeRow(view, cursor.getString(ItemTable.FIELD_TITLE),
dateFormat(date), cursor.getString(ItemTable.FIELD_ARTIST));
CheckBox checkBox = (CheckBox) view.findViewById(R.id.checkbox);
if (enableCheckBox_ && !date.after(new Date())) {
checkBox.setTag(cursor.getString(ItemTable.FIELD_ID));
checkBox.setVisibility(View.VISIBLE);
view.setBackgroundResource(R.layout.selector_item_released);
} else {
checkBox.setVisibility(View.INVISIBLE);
view.setBackgroundResource(0);
}
}
/**
* Create a date object from an SQL formatted date string (yyyy-MM-dd).
*
* Returns a date object set to the current time on failure.
*
* @param sql The SQL formatted date string.
* @return A new date object set to the corresponding date.
*/
private static Date sqlToDate(String sql) {
Date date = null;
try {
date = new SimpleDateFormat("yyyy-MM-dd").parse(sql);
} catch (ParseException e) {
Log.w(TAG, "Failed to parse date.");
e.printStackTrace();
date = new Date();
date.setTime(0);
}
return date;
}
/**
* Convert a date object to a formatted string.
*
* @param date
* Object containing the date to be displayed.
* @return Formatted date, as specified by user preferences.
*/
public static String dateFormat(Date date) {
java.text.DateFormat dateFormat = android.text.format.DateFormat
.getDateFormat(ReleaseTracker.getContext());
return (String) dateFormat.format(date);
}
/**
* Initialize a ListView item with values.
*
* @param v
* The row to be initialized.
* @param title
* String for the title field.
* @param releaseDate
* String for the release date field.
* @param artist
* String for the artist field.
*/
public static void initializeRow(View v, String title, String releaseDate,
String artist) {
TextView titleView = (TextView) v.findViewById(R.id.title);
titleView.setText(title);
TextView releaseDateView = (TextView) v.findViewById(R.id.release_date);
releaseDateView.setText(releaseDate);
TextView artistView = (TextView) v.findViewById(R.id.artist);
artistView.setText(artist);
}
}

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) 2012, Felix Ableitner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.nutomic.releasetracker.sql;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.github.nutomic.releasetracker.ReleaseTracker;
/**
* Singleton that manages SQL database connection.
*
* for threading (put this in background thread):
* http://developer.android.com/reference/android/content/CursorLoader.html
* http://developer.android.com/reference/android/app/LoaderManager.html
* Handler?
*/
public class SqlHelper extends SQLiteOpenHelper {
private static final String TAG = "DB";
/**
* Enum of API types.
*/
public static final int API_ALL = 0;
public static final int API_MOVIE = 1;
public static final int API_TV = 2;
public static final int API_MUSIC = 3;
public static final int API_CUSTOM = 4;
private static SqlHelper instance_ = null;
/**
* Various SQL constants.
*/
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "database.db";
/**
* Opens database with SQL constants.
*
* @inheritDoc
*/
private SqlHelper() {
super(ReleaseTracker.getContext(), DATABASE_NAME, null,
DATABASE_VERSION);
}
/**
* Returns the instance of this class. DO not hold onto this object as it
* may be closed on low memory.
*
* @return Object of this class.
*/
public static SqlHelper getInstance() {
if (instance_ == null) {
instance_ = new SqlHelper();
}
return instance_;
}
/**
* Deletes the current instance to free memory. This function is called by
* {@link ReleaseTracker#onActivityPaused} or
* {@link ReleaseTracker#onLowMemory()} and should not be called elsewhere.
*/
public static void deleteInstance() {
if (instance_ != null) {
instance_.close();
instance_ = null;
}
}
/**
* Create tables.
*/
@Override
public void onCreate(SQLiteDatabase db) {
Log.i(TAG, "Initializing Database.");
db.execSQL(ItemTable.getCreateTableString());
db.execSQL(PersonTable.getCreateTableString());
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// pass
}
}