Utils.java revision f8367f0d7dc6abc4876b52e04e2530c06464698e
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.tv.util;
18
19import android.content.ComponentName;
20import android.content.ContentUris;
21import android.content.Context;
22import android.content.Intent;
23import android.content.SharedPreferences;
24import android.content.pm.ActivityInfo;
25import android.content.pm.PackageManager;
26import android.content.pm.ResolveInfo;
27import android.database.Cursor;
28import android.media.tv.TvContract;
29import android.media.tv.TvInputInfo;
30import android.net.Uri;
31import android.preference.PreferenceManager;
32import android.text.TextUtils;
33import android.util.Base64;
34
35import com.android.tv.data.Channel;
36import com.android.tv.data.Program;
37import com.android.tv.data.StreamInfo;
38
39import java.util.List;
40
41/**
42 * A class that includes convenience methods for accessing TvProvider database.
43 */
44public class Utils {
45    public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService";
46    public static final String EXTRA_SERVICE_NAME = "serviceName";
47    public static final String EXTRA_KEYCODE = "keycode";
48
49    public static final String CHANNEL_SORT_ORDER_BY_INPUT_NAME =
50            TvContract.Channels.COLUMN_PACKAGE_NAME + ", "
51            + TvContract.Channels.COLUMN_SERVICE_NAME;
52
53    public static final String CHANNEL_SORT_ORDER_BY_DISPLAY_NUMBER =
54            "CAST(" + TvContract.Channels.COLUMN_DISPLAY_NUMBER + " AS INTEGER), "
55            + "CAST(SUBSTR(LTRIM(" + TvContract.Channels.COLUMN_DISPLAY_NUMBER
56            + ",'0123456789'),2) AS INTEGER)";
57
58    // preferences stored in the default preference.
59    private static final String PREF_KEY_LAST_SELECTED_TV_INPUT = "last_selected_tv_input";
60
61    private static final String PREFIX_PREF_NAME = "com.android.tv.";
62    // preferences stored in the preference of a specific tv input.
63    private static final String PREF_KEY_LAST_WATCHED_CHANNEL_ID = "last_watched_channel_id";
64
65    private static int VIDEO_SD_WIDTH = 704;
66    private static int VIDEO_SD_HEIGHT = 480;
67    private static int VIDEO_HD_WIDTH = 1280;
68    private static int VIDEO_HD_HEIGHT = 720;
69    private static int VIDEO_FULL_HD_WIDTH = 1920;
70    private static int VIDEO_FULL_HD_HEIGHT = 1080;
71    private static int VIDEO_ULTRA_HD_WIDTH = 2048;
72    private static int VIDEO_ULTRA_HD_HEIGHT = 1536;
73
74    // TODO: Remove this and add inputId into TvProvider.
75    public static String getInputIdForComponentName(ComponentName name) {
76        return name.flattenToShortString();
77    }
78
79    public static Uri getChannelUri(long channelId) {
80        return ContentUris.withAppendedId(TvContract.Channels.CONTENT_URI, channelId);
81    }
82
83    public static String getInputIdForChannel(Context context, long channelId) {
84        if (channelId == Channel.INVALID_ID) {
85            return null;
86        }
87        Uri channelUri = ContentUris.withAppendedId(TvContract.Channels.CONTENT_URI, channelId);
88        return getInputIdForChannel(context, channelUri);
89    }
90
91    public static String getInputIdForChannel(Context context, Uri channelUri) {
92        String[] projection = { TvContract.Channels.COLUMN_PACKAGE_NAME,
93                TvContract.Channels.COLUMN_SERVICE_NAME };
94        if (channelUri == null) {
95            return null;
96        }
97        Cursor cursor = context.getContentResolver().query(
98                channelUri, projection, null, null, null);
99        if (cursor == null) {
100            return null;
101        }
102        if (cursor.getCount() < 1) {
103            cursor.close();
104            return null;
105        }
106        cursor.moveToNext();
107        ComponentName componentName = new ComponentName(cursor.getString(0), cursor.getString(1));
108        cursor.close();
109        return getInputIdForComponentName(componentName);
110    }
111
112    public static void setLastWatchedChannelId(Context context, String inputId, long channelId) {
113        if (TextUtils.isEmpty(inputId)) {
114            throw new IllegalArgumentException("inputId cannot be empty");
115        }
116        context.getSharedPreferences(getPreferenceName(inputId), Context.MODE_PRIVATE).edit()
117                .putLong(PREF_KEY_LAST_WATCHED_CHANNEL_ID, channelId).apply();
118        PreferenceManager.getDefaultSharedPreferences(context).edit()
119                .putString(PREF_KEY_LAST_SELECTED_TV_INPUT, inputId).apply();
120    }
121
122    public static long getLastWatchedChannelId(Context context) {
123        String inputId = getLastSelectedInputId(context);
124        if (inputId == null) {
125            return Channel.INVALID_ID;
126        }
127        return getLastWatchedChannelId(context, inputId);
128    }
129
130    public static long getLastWatchedChannelId(Context context, String inputId) {
131        if (TextUtils.isEmpty(inputId)) {
132            throw new IllegalArgumentException("inputId cannot be empty");
133        }
134        return context.getSharedPreferences(getPreferenceName(inputId),
135                Context.MODE_PRIVATE).getLong(PREF_KEY_LAST_WATCHED_CHANNEL_ID, Channel.INVALID_ID);
136    }
137
138    public static String getLastSelectedInputId(Context context) {
139        return PreferenceManager.getDefaultSharedPreferences(context)
140                .getString(PREF_KEY_LAST_SELECTED_TV_INPUT, null);
141    }
142
143    public static Program getCurrentProgram(Context context, Uri channelUri) {
144        if (channelUri == null) {
145            return null;
146        }
147        long time = System.currentTimeMillis();
148        Uri uri = TvContract.buildProgramsUriForChannel(channelUri, time, time);
149        String[] projection = {
150                TvContract.Programs.COLUMN_TITLE,
151                TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
152                TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
153                TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS };
154        Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
155        String title = null;
156        String description = null;
157        long startTime = -1;
158        long endTime = -1;
159        if (cursor.moveToNext()) {
160            title = cursor.getString(0);
161            description = cursor.getString(1);
162            startTime = cursor.getLong(2);
163            endTime = cursor.getLong(3);
164        }
165        cursor.close();
166
167        // TODO: Consider providing the entire data if needed.
168        return new Program.Builder()
169                .setTitle(title)
170                .setDescription(description)
171                .setStartTimeUtcMillis(startTime)
172                .setEndTimeUtcMillis(endTime).build();
173    }
174
175    public static boolean hasChannel(Context context, TvInputInfo name) {
176        return hasChannel(context, name, true);
177    }
178
179    public static boolean hasChannel(Context context, TvInputInfo name, boolean browsableOnly) {
180        Uri uri = TvContract.buildChannelsUriForInput(name.getComponent(), browsableOnly);
181        String[] projection = { TvContract.Channels._ID };
182        Cursor cursor = null;
183        try {
184            cursor = context.getContentResolver().query(uri, projection, null, null, null);
185            return cursor != null && cursor.getCount() > 0;
186        } finally {
187            if (cursor != null) {
188                cursor.close();
189            }
190        }
191    }
192
193    public static SharedPreferences getSharedPreferencesOfDisplayNameForInput(Context context) {
194        return context.getSharedPreferences(TvSettings.PREFS_FILE, Context.MODE_PRIVATE);
195    }
196
197    public static String getDisplayNameForInput(Context context, TvInputInfo info) {
198        SharedPreferences preferences = getSharedPreferencesOfDisplayNameForInput(context);
199        PackageManager pm = context.getPackageManager();
200        return preferences.getString(TvSettings.PREF_DISPLAY_INPUT_NAME + info.getId(),
201                info.loadLabel(pm).toString());
202    }
203
204    public static boolean hasActivity(Context context, TvInputInfo input, String action) {
205        return getActivityInfo(context, input, action) != null;
206    }
207
208    public static int getVideoDefinitionLevelFromSize(int width, int height) {
209        if (width >= VIDEO_ULTRA_HD_WIDTH && height >= VIDEO_ULTRA_HD_HEIGHT) {
210            return StreamInfo.VIDEO_DEFINITION_LEVEL_ULTRA_HD;
211        } else if (width >= VIDEO_FULL_HD_WIDTH && height >= VIDEO_FULL_HD_HEIGHT) {
212            return StreamInfo.VIDEO_DEFINITION_LEVEL_FULL_HD;
213        } else if (width >= VIDEO_HD_WIDTH && height >= VIDEO_HD_HEIGHT) {
214            return StreamInfo.VIDEO_DEFINITION_LEVEL_HD;
215        } else if (width >= VIDEO_SD_WIDTH && height >= VIDEO_SD_HEIGHT) {
216            return StreamInfo.VIDEO_DEFINITION_LEVEL_SD;
217        }
218        return StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN;
219    }
220
221    public static String getVideoDefinitionLevelString(int videoFormat) {
222        switch (videoFormat) {
223            case StreamInfo.VIDEO_DEFINITION_LEVEL_ULTRA_HD:
224                return "UltraHD";
225            case StreamInfo.VIDEO_DEFINITION_LEVEL_FULL_HD:
226                return "FullHD";
227            case StreamInfo.VIDEO_DEFINITION_LEVEL_HD:
228                return "HD";
229            case StreamInfo.VIDEO_DEFINITION_LEVEL_SD:
230                return "SD";
231        }
232        return "";
233    }
234
235    public static String getAudioChannelString(int channelCount) {
236        switch (channelCount) {
237            case 1:
238                return "MONO";
239            case 2:
240                return "STEREO";
241            case 6:
242                return "5.1";
243            case 8:
244                return "7.1";
245        }
246        return "";
247    }
248
249    private static ActivityInfo getActivityInfo(Context context, TvInputInfo input, String action) {
250        if (input == null) {
251            return null;
252        }
253
254        List<ResolveInfo> infos = context.getPackageManager().queryIntentActivities(
255                new Intent(action), PackageManager.GET_ACTIVITIES);
256        if (infos == null) {
257            return null;
258        }
259
260        for (ResolveInfo info : infos) {
261            if (info.activityInfo.packageName.equals(input.getPackageName())) {
262                return info.activityInfo;
263            }
264        }
265        return null;
266    }
267
268    private static String getPreferenceName(String inputId) {
269        return PREFIX_PREF_NAME + Base64.encodeToString(inputId.getBytes(), Base64.URL_SAFE);
270    }
271}
272