PageRenderTime 119ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/app/src/main/java/org/xbmc/android/app/io/video/MovieDetailsHandler.java

https://github.com/freezy/android-xbmcremote-sandbox
Java | 251 lines | 159 code | 25 blank | 67 comment | 28 complexity | 50a84c5153308a68c3da1c9871e93595 MD5 | raw file
  1. /*
  2. * Copyright (C) 2005-2015 Team XBMC
  3. * http://xbmc.org
  4. *
  5. * This Program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2, or (at your option)
  8. * any later version.
  9. *
  10. * This Program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with XBMC Remote; see the file license. If not, write to
  17. * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. */
  21. package org.xbmc.android.app.io.video;
  22. import android.content.ContentResolver;
  23. import android.content.ContentValues;
  24. import android.database.Cursor;
  25. import android.net.Uri;
  26. import android.os.Parcel;
  27. import android.provider.BaseColumns;
  28. import org.codehaus.jackson.JsonNode;
  29. import org.codehaus.jackson.node.ArrayNode;
  30. import org.codehaus.jackson.node.ObjectNode;
  31. import org.xbmc.android.app.provider.VideoContract;
  32. import org.xbmc.android.jsonrpc.api.AbstractCall;
  33. import org.xbmc.android.jsonrpc.api.call.VideoLibrary;
  34. import org.xbmc.android.jsonrpc.api.model.VideoModel;
  35. import org.xbmc.android.jsonrpc.io.JsonHandler;
  36. import org.xbmc.android.util.DBUtils;
  37. import java.util.HashMap;
  38. import java.util.HashSet;
  39. import static org.xbmc.android.jsonrpc.api.model.VideoModel.MovieDetail;
  40. /**
  41. * Handles one-way synchronization between XBMC's <tt>movie</tt> table and the local
  42. * {@link org.xbmc.android.app.provider.VideoContract.Movies} table.
  43. *
  44. * @author freezy <freezy@xbmc.org>
  45. */
  46. public class MovieDetailsHandler extends JsonHandler {
  47. private final static String TAG = MovieDetailsHandler.class.getSimpleName();
  48. private static HashMap<String, Integer> PEOPLE_CACHE;
  49. private static HashMap<String, Integer> GENRE_CACHE;
  50. /**
  51. * Directors and writers don't have thumbs - need to know when to update a person that hasn't a thumbnail
  52. * and might was added by director/writer and not via actor.
  53. */
  54. private static HashSet<Integer> THUMB_CACHE;
  55. private final int id;
  56. private final int hostId;
  57. public MovieDetailsHandler(int hostId, int id) {
  58. super(VideoContract.CONTENT_AUTHORITY);
  59. this.id = id;
  60. this.hostId = hostId;
  61. }
  62. public static void initCache() {
  63. PEOPLE_CACHE = null;
  64. GENRE_CACHE = null;
  65. THUMB_CACHE = null;
  66. }
  67. @Override
  68. protected ContentValues[] parse(JsonNode response, ContentResolver resolver) {
  69. /* SETUP CACHE VARIABLES
  70. */
  71. if (PEOPLE_CACHE == null) {
  72. PEOPLE_CACHE = new HashMap<String, Integer>();
  73. THUMB_CACHE = new HashSet<Integer>();
  74. final String[] model = { BaseColumns._ID, VideoContract.People.NAME, VideoContract.People.THUMBNAIL };
  75. final Cursor cursor = resolver.query(VideoContract.People.CONTENT_URI, model, null, null, null);
  76. while (cursor.moveToNext()) {
  77. final int id = cursor.getInt(0);
  78. PEOPLE_CACHE.put(cursor.getString(1), id);
  79. if (cursor.getString(2) != null) {
  80. THUMB_CACHE.add(id);
  81. }
  82. }
  83. }
  84. // put genres into cache
  85. if (GENRE_CACHE == null) {
  86. GENRE_CACHE = new HashMap<String, Integer>();
  87. final String[] model = { BaseColumns._ID, VideoContract.Genres.NAME };
  88. final Cursor cursor = resolver.query(VideoContract.Genres.CONTENT_URI, model, null, null, null);
  89. while (cursor.moveToNext()) {
  90. GENRE_CACHE.put(cursor.getString(1), cursor.getInt(0));
  91. }
  92. }
  93. // retrieve object
  94. final ObjectNode movie = (ObjectNode)response.get(AbstractCall.RESULT).get(VideoLibrary.GetMovieDetails.RESULT);
  95. /* CAST
  96. */
  97. final ArrayNode cast = (ArrayNode)movie.get(MovieDetail.CAST);
  98. final ContentValues[] batch = new ContentValues[cast.size()];
  99. int i = 0;
  100. for (JsonNode actor : cast) {
  101. final String name = actor.get(VideoModel.Cast.NAME).getTextValue();
  102. if (name.isEmpty()) {
  103. continue;
  104. }
  105. batch[i] = new ContentValues();
  106. batch[i].put(VideoContract.MovieCast.MOVIE_REF, id);
  107. batch[i].put(VideoContract.MovieCast.PERSON_REF, getPerson(resolver, name, actor));
  108. batch[i].put(VideoContract.MovieCast.ROLE, DBUtils.getStringValue(actor, VideoModel.Cast.ROLE));
  109. batch[i].put(VideoContract.MovieCast.SORT, DBUtils.getIntValue(actor, VideoModel.Cast.ORDER));
  110. i++;
  111. }
  112. /* DIRECTORS
  113. */
  114. final ArrayNode directors = (ArrayNode)movie.get(MovieDetail.DIRECTOR);
  115. if (directors != null) {
  116. for (JsonNode director : directors) {
  117. final ContentValues row = new ContentValues();
  118. row.put(VideoContract.MovieDirector.MOVIE_REF, id);
  119. row.put(VideoContract.MovieDirector.PERSON_REF, getPerson(resolver, director.getTextValue()));
  120. resolver.insert(VideoContract.MovieDirector.CONTENT_URI, row);
  121. }
  122. }
  123. /* WRITERS
  124. */
  125. final ArrayNode writers = (ArrayNode)movie.get(MovieDetail.WRITER);
  126. if (writers != null) {
  127. for (JsonNode writer : writers) {
  128. final ContentValues row = new ContentValues();
  129. row.put(VideoContract.MovieWriter.MOVIE_REF, id);
  130. row.put(VideoContract.MovieWriter.PERSON_REF, getPerson(resolver, writer.getTextValue()));
  131. resolver.insert(VideoContract.MovieWriter.CONTENT_URI, row);
  132. }
  133. }
  134. /* GENRES
  135. */
  136. final ArrayNode genres = (ArrayNode)movie.get(MovieDetail.GENRE);
  137. for (JsonNode genre : genres) {
  138. final String name = genre.getTextValue();
  139. final int genreRef;
  140. if (GENRE_CACHE.containsKey(name)) {
  141. genreRef = GENRE_CACHE.get(name);
  142. } else {
  143. final ContentValues row = new ContentValues();
  144. row.put(VideoContract.Genres.NAME, name);
  145. final Uri newGenreUri = resolver.insert(VideoContract.Genres.CONTENT_URI, row);
  146. genreRef = VideoContract.Genres.getGenreId(newGenreUri);
  147. GENRE_CACHE.put(name, genreRef);
  148. }
  149. // insert reference
  150. final ContentValues row = new ContentValues();
  151. row.put(VideoContract.MovieGenres.GENRE_REF, genreRef);
  152. row.put(VideoContract.MovieGenres.MOVIE_REF, id);
  153. resolver.insert(VideoContract.MovieGenres.CONTENT_URI, row);
  154. }
  155. return batch;
  156. }
  157. /**
  158. * Returns the ID of a person with a given name.
  159. *
  160. * Firstly, the cache is checked. If miss, it's added to the database and the ID is returned.
  161. * If the "person" parameter is passed and the current record doesn't contain a thumbnail (but the
  162. * "person" node does), the current record is updated with the thumbnail. This can happen if
  163. * a record is added for a director (which doesn't contain any more data) and only later is checked
  164. * for a cast node (which does contain the thumb).
  165. *
  166. * @param resolver Content resolver
  167. * @param name Name of the person
  168. * @param person If cast, the node that contains additional metadata. Can be null.
  169. * @return Database ID of the person
  170. */
  171. private int getPerson(ContentResolver resolver, String name, JsonNode person) {
  172. final int personRef;
  173. if (PEOPLE_CACHE.containsKey(name)) {
  174. personRef = PEOPLE_CACHE.get(name);
  175. // update thumb if necessary
  176. if (person != null && person.has(VideoModel.Cast.THUMBNAIL) && !THUMB_CACHE.contains(personRef)) {
  177. final ContentValues row = new ContentValues();
  178. row.put(VideoContract.People.THUMBNAIL, person.get(VideoModel.Cast.THUMBNAIL).getTextValue());
  179. final String[] args = { String.valueOf(personRef) };
  180. resolver.update(VideoContract.People.CONTENT_URI, row, BaseColumns._ID + "=?", args);
  181. }
  182. } else {
  183. final ContentValues row = new ContentValues();
  184. row.put(VideoContract.People.HOST_ID, hostId);
  185. row.put(VideoContract.People.NAME, name);
  186. if (person != null && person.has(VideoModel.Cast.THUMBNAIL)) {
  187. row.put(VideoContract.People.THUMBNAIL, person.get(VideoModel.Cast.THUMBNAIL).getTextValue());
  188. }
  189. final Uri newPersonUri = resolver.insert(VideoContract.People.CONTENT_URI, row);
  190. personRef = VideoContract.People.getPersonId(newPersonUri);
  191. PEOPLE_CACHE.put(name, personRef);
  192. }
  193. return personRef;
  194. }
  195. /**
  196. * Returns the ID of a person with a given name. If not in the database, the person will be added.
  197. * @param resolver Content
  198. * @param name Name of the person
  199. * @return Database ID of the person
  200. */
  201. private int getPerson(ContentResolver resolver, String name) {
  202. return getPerson(resolver, name, null);
  203. }
  204. @Override
  205. protected void insert(ContentResolver resolver, ContentValues[] batch) {
  206. resolver.bulkInsert(VideoContract.MovieCast.CONTENT_URI, batch);
  207. }
  208. @Override
  209. public void writeToParcel(Parcel parcel, int flags) {
  210. parcel.writeLong(hostId);
  211. parcel.writeLong(id);
  212. }
  213. /**
  214. * Generates instances of this Parcelable class from a Parcel.
  215. */
  216. public static final Creator<MovieDetailsHandler> CREATOR = new Creator<MovieDetailsHandler>() {
  217. @Override
  218. public MovieDetailsHandler createFromParcel(Parcel parcel) {
  219. return new MovieDetailsHandler(parcel.readInt(), parcel.readInt());
  220. }
  221. @Override
  222. public MovieDetailsHandler[] newArray(int n) {
  223. return new MovieDetailsHandler[n];
  224. }
  225. };
  226. }