Download File
Download Project
Settings
Line Wrap
Themes
default
ambiance
bespin
dracula
eclipse
material
mbo
mdn-like
neat
solarized dark
ttcn
zenburn
InternalProviderData.java
/* * Copyright 2016 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.media.tv.companionlibrary.model; import android.support.annotation.NonNull; import com.google.android.media.tv.companionlibrary.utils.TvContractUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * This is a serialized class used for storing and retrieving serialized data from * {@link android.media.tv.TvContract.Channels#COLUMN_INTERNAL_PROVIDER_DATA}, * {@link android.media.tv.TvContract.Programs#COLUMN_INTERNAL_PROVIDER_DATA}, and * {@link android.media.tv.TvContract.RecordedPrograms#COLUMN_INTERNAL_PROVIDER_DATA}. * * In addition to developers being able to add custom attributes to this data type, there are * pre-defined values. */ public class InternalProviderData { private static final String TAG = "InternalProviderData"; private static final boolean DEBUG = true; private static final String KEY_VIDEO_TYPE = "type"; private static final String KEY_VIDEO_URL = "url"; private static final String KEY_REPEATABLE = "repeatable"; private static final String KEY_CUSTOM_DATA = "custom"; private static final String KEY_ADVERTISEMENTS = "advertisements"; private static final String KEY_ADVERTISEMENT_START = "start"; private static final String KEY_ADVERTISEMENT_STOP = "stop"; private static final String KEY_ADVERTISEMENT_TYPE = "type"; private static final String KEY_ADVERTISEMENT_REQUEST_URL = "requestUrl"; private static final String KEY_RECORDING_START_TIME = "recordingStartTime"; private JSONObject mJsonObject; /** * Creates a new empty object */ public InternalProviderData() { mJsonObject = new JSONObject(); } /** * Creates a new object and attempts to populate from the provided String * * @param data Correctly formatted InternalProviderData * @throws ParseException If data is not formatted correctly */ public InternalProviderData(@NonNull String data) throws ParseException { try { mJsonObject = new JSONObject(data); } catch (JSONException e) { throw new ParseException(e.getMessage()); } } /** * Creates a new object and attempts to populate by obtaining the String representation of the * provided byte array * * @param bytes Byte array corresponding to a correctly formatted String representation of * InternalProviderData * @throws ParseException If data is not formatted correctly */ public InternalProviderData(@NonNull byte[] bytes) throws ParseException { try { mJsonObject = new JSONObject(new String(bytes)); } catch (JSONException e) { throw new ParseException(e.getMessage()); } } private int jsonHash(JSONObject jsonObject) { int hashSum = 0; Iterator
keys = jsonObject.keys(); while(keys.hasNext()) { String key = keys.next(); try { if (jsonObject.get(key) instanceof JSONObject) { // This is a branch, get hash of this object recursively JSONObject branch = jsonObject.getJSONObject(key); hashSum += jsonHash(branch); } else { // If this key does not link to a JSONObject, get hash of leaf hashSum += key.hashCode() + jsonObject.get(key).hashCode(); } } catch (JSONException ignored) { } } return hashSum; } @Override public int hashCode() { // Recursively get the hashcode from all internal JSON keys and values return jsonHash(mJsonObject); } private boolean jsonEquals(JSONObject json1, JSONObject json2) { Iterator
keys = json1.keys(); while(keys.hasNext()) { String key = keys.next(); try { if (json1.get(key) instanceof JSONObject) { // This is a branch, check equality of this object recursively JSONObject thisBranch = json1.getJSONObject(key); JSONObject otherBranch = json2.getJSONObject(key); return jsonEquals(thisBranch, otherBranch); } else { // If this key does not link to a JSONObject, check equality of leaf if (!json1.get(key).equals(json2.get(key))) { // The VALUE of the KEY does not match return false; } } } catch (JSONException e) { return false; } } // Confirm that no key has been missed in the check return json1.length() == json2.length(); } /** * Tests that the value of each key is equal. Order does not matter. * * @param obj The object you are comparing to. * @return Whether the value of each key between both objects is equal. */ @Override public boolean equals(Object obj) { if (obj == null || ! (obj instanceof InternalProviderData)) { return false; } JSONObject otherJsonObject = ((InternalProviderData) obj).mJsonObject; return jsonEquals(mJsonObject, otherJsonObject); } @Override public String toString() { return mJsonObject.toString(); } /** * Gets the video type of the program. * * @return The video type of the program, -1 if no value has been given. */ public int getVideoType() { if (mJsonObject.has(KEY_VIDEO_TYPE)) { try { return mJsonObject.getInt(KEY_VIDEO_TYPE); } catch (JSONException ignored) { } } return TvContractUtils.SOURCE_TYPE_INVALID; } /** * Sets the video type of the program. * * @param videoType The video source type. Could be {@link TvContractUtils#SOURCE_TYPE_HLS}, * {@link TvContractUtils#SOURCE_TYPE_HTTP_PROGRESSIVE}, * or {@link TvContractUtils#SOURCE_TYPE_MPEG_DASH}. */ public void setVideoType(int videoType) { try { mJsonObject.put(KEY_VIDEO_TYPE, videoType); } catch (JSONException ignored) { } } /** * Gets the video url of the program if valid. * * @return The video url of the program if valid, null if no value has been given. */ public String getVideoUrl() { if (mJsonObject.has(KEY_VIDEO_URL)) { try { return mJsonObject.getString(KEY_VIDEO_URL); } catch (JSONException ignored) { } } return null; } /** * Gets a list of all advertisements. If no ads have been assigned, the list will be empty. * * @return A list of all advertisements for this channel or program. */ public List
getAds() { List
ads = new ArrayList<>(); try { if (mJsonObject.has(KEY_ADVERTISEMENTS)) { JSONArray adsJsonArray = new JSONArray(mJsonObject.get(KEY_ADVERTISEMENTS).toString()); for (int i = 0; i < adsJsonArray.length(); i++) { JSONObject ad = adsJsonArray.getJSONObject(i); long start = ad.getLong(KEY_ADVERTISEMENT_START); long stop = ad.getLong(KEY_ADVERTISEMENT_STOP); int type = ad.getInt(KEY_ADVERTISEMENT_TYPE); String requestUrl = ad.getString(KEY_ADVERTISEMENT_REQUEST_URL); ads.add(new Advertisement.Builder() .setStartTimeUtcMillis(start) .setStopTimeUtcMillis(stop) .setType(type) .setRequestUrl(requestUrl) .build()); } } } catch (JSONException ignored) { } return ads; } /** * Gets recording start time of program for recorded program. For a non-recorded program, this * value will not be set. * * @return Recording start of program in UTC milliseconds, 0 if no value is given. */ public long getRecordedProgramStartTime() { try { return mJsonObject.getLong(KEY_RECORDING_START_TIME); } catch (JSONException ignored) { } return 0; } /** * Sets the video url of the program. * * @param videoUrl A valid url pointing to the video to be played. */ public void setVideoUrl(String videoUrl) { try { mJsonObject.put(KEY_VIDEO_URL, videoUrl); } catch (JSONException ignored) { } } /** * Checks whether the programs on this channel should be repeated periodically in order. * * @return Whether to repeat programs. Returns false if no value has been set. */ public boolean isRepeatable() { if (mJsonObject.has(KEY_REPEATABLE)) { try { return mJsonObject.getBoolean(KEY_REPEATABLE); } catch (JSONException ignored) { } } return false; } /** * Sets whether programs assigned to this channel should be repeated periodically. * This field is relevant to channels. * * @param repeatable Whether to repeat programs. */ public void setRepeatable(boolean repeatable) { try { mJsonObject.put(KEY_REPEATABLE, repeatable); } catch (JSONException ignored) { } } /** * Sets a list of advertisements for this channel or program. If setting for a channel, list * size should be <= 1. Channels cannot have more than one advertisement. * * @param ads A list of advertisements that should be shown. */ public void setAds(List
ads) { try { if (ads != null && !ads.isEmpty()) { JSONArray adsJsonArray = new JSONArray(); for (Advertisement ad : ads) { JSONObject adJson = new JSONObject(); adJson.put(KEY_ADVERTISEMENT_START, ad.getStartTimeUtcMillis()); adJson.put(KEY_ADVERTISEMENT_STOP, ad.getStopTimeUtcMillis()); adJson.put(KEY_ADVERTISEMENT_TYPE, ad.getType()); adJson.put(KEY_ADVERTISEMENT_REQUEST_URL, ad.getRequestUrl()); adsJsonArray.put(adJson); } mJsonObject.put(KEY_ADVERTISEMENTS, adsJsonArray); } } catch (JSONException ignored) { } } /** * Sets the recording program start time for a recorded program. * * @param startTime Recording start time in UTC milliseconds of recorded program. */ public void setRecordingStartTime(long startTime) { try { mJsonObject.put(KEY_RECORDING_START_TIME, startTime); } catch (JSONException ignored) { } } /** * Adds some custom data to the InternalProviderData. * * @param key The key for this data * @param value The value this data should take * @return This InternalProviderData object to allow for chaining of calls * @throws ParseException If there is a problem adding custom data */ public InternalProviderData put(String key, Object value) throws ParseException { try { if (!mJsonObject.has(KEY_CUSTOM_DATA)) { mJsonObject.put(KEY_CUSTOM_DATA, new JSONObject()); } mJsonObject.getJSONObject(KEY_CUSTOM_DATA).put(key, String.valueOf(value)); } catch (JSONException e) { throw new ParseException(e.getMessage()); } return this; } /** * Gets some previously added custom data stored in InternalProviderData. * * @param key The key assigned to this data * @return The value of this key if it has been defined. Returns null if the key is not found. * @throws ParseException If there is a problem getting custom data */ public Object get(String key) throws ParseException { if (! mJsonObject.has(KEY_CUSTOM_DATA)) { return null; } try { return mJsonObject.getJSONObject(KEY_CUSTOM_DATA).opt(key); } catch (JSONException e) { throw new ParseException(e.getMessage()); } } /** * Checks whether a custom key is found in InternalProviderData. * * @param key The key assigned to this data * @return Whether this key is found. * @throws ParseException If there is a problem checking custom data */ public boolean has(String key) throws ParseException { if (! mJsonObject.has(KEY_CUSTOM_DATA)) { return false; } try { return mJsonObject.getJSONObject(KEY_CUSTOM_DATA).has(key); } catch (JSONException e) { throw new ParseException(e.getMessage()); } } /** * This exception is thrown when an error occurs in getting or setting data for the * InternalProviderData. */ public class ParseException extends JSONException { public ParseException(String s) { super(s); } } }