diff options
Diffstat (limited to 'src/com/android/tv/util')
34 files changed, 1307 insertions, 1727 deletions
diff --git a/src/com/android/tv/util/AsyncDbTask.java b/src/com/android/tv/util/AsyncDbTask.java index 477412e4..60fa3018 100644 --- a/src/com/android/tv/util/AsyncDbTask.java +++ b/src/com/android/tv/util/AsyncDbTask.java @@ -27,24 +27,20 @@ import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; import android.util.Log; import android.util.Range; - +import com.android.tv.TvSingletons; +import com.android.tv.common.BuildConfig; import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; +import com.android.tv.data.ChannelImpl; import com.android.tv.data.Program; +import com.android.tv.data.api.Channel; import com.android.tv.dvr.data.RecordedProgram; - import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.Executor; /** * {@link AsyncTask} that defaults to executing on its own single threaded Executor Service. * - * <p>Instances of this class should only be executed this using {@link - * #executeOnDbThread(Object[])}. - * * @param <Params> the type of the parameters sent to the task upon execution. * @param <Progress> the type of the progress units published during the background computation. * @param <Result> the type of the result of the background computation. @@ -54,38 +50,19 @@ public abstract class AsyncDbTask<Params, Progress, Result> private static final String TAG = "AsyncDbTask"; private static final boolean DEBUG = false; - private static final NamedThreadFactory THREAD_FACTORY = new NamedThreadFactory( - AsyncDbTask.class.getSimpleName()); - private static final ExecutorService DB_EXECUTOR = Executors - .newSingleThreadExecutor(THREAD_FACTORY); + private final Executor mExecutor; + boolean mCalledExecuteOnDbThread; - /** - * Returns the single tread executor used for DbTasks. - */ - public static ExecutorService getExecutor() { - return DB_EXECUTOR; - } - - /** - * Executes the given command at some time in the future. - * - * <p>The command will be executed by {@link #getExecutor()}. - * - * @param command the runnable task - * @throws RejectedExecutionException if this task cannot be - * accepted for execution - * @throws NullPointerException if command is null - */ - public static void executeOnDbThread(Runnable command) { - DB_EXECUTOR.execute(command); + protected AsyncDbTask(Executor mExecutor) { + this.mExecutor = mExecutor; } /** * Returns the result of a {@link ContentResolver#query(Uri, String[], String, String[], * String)}. * - * <p> {@link #doInBackground(Void...)} executes the query on call {@link #onQuery(Cursor)} - * which is implemented by subclasses. + * <p>{@link #doInBackground(Void...)} executes the query on call {@link #onQuery(Cursor)} which + * is implemented by subclasses. * * @param <Result> the type of result returned by {@link #onQuery(Cursor)} */ @@ -97,9 +74,15 @@ public abstract class AsyncDbTask<Params, Progress, Result> private final String[] mSelectionArgs; private final String mOrderBy; - - public AsyncQueryTask(ContentResolver contentResolver, Uri uri, String[] projection, - String selection, String[] selectionArgs, String orderBy) { + public AsyncQueryTask( + Executor executor, + ContentResolver contentResolver, + Uri uri, + String[] projection, + String selection, + String[] selectionArgs, + String orderBy) { + super(executor); mContentResolver = contentResolver; mUri = uri; mProjection = projection; @@ -110,13 +93,15 @@ public abstract class AsyncDbTask<Params, Progress, Result> @Override protected final Result doInBackground(Void... params) { - if (!THREAD_FACTORY.namedWithPrefix(Thread.currentThread())) { - IllegalStateException e = new IllegalStateException(this - + " should only be executed using executeOnDbThread, " - + "but it was called on thread " - + Thread.currentThread()); + if (!mCalledExecuteOnDbThread) { + IllegalStateException e = + new IllegalStateException( + this + + " should only be executed using executeOnDbThread, " + + "but it was called on thread " + + Thread.currentThread()); Log.w(TAG, e); - if (DEBUG) { + if (BuildConfig.ENG) { throw e; } } @@ -128,8 +113,9 @@ public abstract class AsyncDbTask<Params, Progress, Result> if (DEBUG) { Log.v(TAG, "Starting query for " + this); } - try (Cursor c = mContentResolver - .query(mUri, mProjection, mSelection, mSelectionArgs, mOrderBy)) { + try (Cursor c = + mContentResolver.query( + mUri, mProjection, mSelection, mSelectionArgs, mOrderBy)) { if (c != null && !isCancelled()) { Result result = onQuery(c); if (DEBUG) { @@ -147,7 +133,7 @@ public abstract class AsyncDbTask<Params, Progress, Result> return null; } } catch (Exception e) { - SoftPreconditions.warn(TAG, null, "Error querying " + this, e); + SoftPreconditions.warn(TAG, null, e, "Error querying " + this); return null; } } @@ -176,14 +162,35 @@ public abstract class AsyncDbTask<Params, Progress, Result> public abstract static class AsyncQueryListTask<T> extends AsyncQueryTask<List<T>> { private final CursorFilter mFilter; - public AsyncQueryListTask(ContentResolver contentResolver, Uri uri, String[] projection, - String selection, String[] selectionArgs, String orderBy) { - this(contentResolver, uri, projection, selection, selectionArgs, orderBy, null); + public AsyncQueryListTask( + Executor executor, + ContentResolver contentResolver, + Uri uri, + String[] projection, + String selection, + String[] selectionArgs, + String orderBy) { + this( + executor, + contentResolver, + uri, + projection, + selection, + selectionArgs, + orderBy, + null); } - public AsyncQueryListTask(ContentResolver contentResolver, Uri uri, String[] projection, - String selection, String[] selectionArgs, String orderBy, CursorFilter filter) { - super(contentResolver, uri, projection, selection, selectionArgs, orderBy); + public AsyncQueryListTask( + Executor executor, + ContentResolver contentResolver, + Uri uri, + String[] projection, + String selection, + String[] selectionArgs, + String orderBy, + CursorFilter filter) { + super(executor, contentResolver, uri, projection, selection, selectionArgs, orderBy); mFilter = filter; } @@ -228,9 +235,15 @@ public abstract class AsyncDbTask<Params, Progress, Result> */ public abstract static class AsyncQueryItemTask<T> extends AsyncQueryTask<T> { - public AsyncQueryItemTask(ContentResolver contentResolver, Uri uri, String[] projection, - String selection, String[] selectionArgs, String orderBy) { - super(contentResolver, uri, projection, selection, selectionArgs, orderBy); + public AsyncQueryItemTask( + Executor executor, + ContentResolver contentResolver, + Uri uri, + String[] projection, + String selection, + String[] selectionArgs, + String orderBy) { + super(executor, contentResolver, uri, projection, selection, selectionArgs, orderBy); } @Override @@ -251,7 +264,6 @@ public abstract class AsyncDbTask<Params, Progress, Result> } return null; } - } /** @@ -268,33 +280,55 @@ public abstract class AsyncDbTask<Params, Progress, Result> protected abstract T fromCursor(Cursor c); } - /** - * Gets an {@link List} of {@link Channel}s from {@link TvContract.Channels#CONTENT_URI}. - */ + /** Gets an {@link List} of {@link Channel}s from {@link TvContract.Channels#CONTENT_URI}. */ public abstract static class AsyncChannelQueryTask extends AsyncQueryListTask<Channel> { - public AsyncChannelQueryTask(ContentResolver contentResolver) { - super(contentResolver, TvContract.Channels.CONTENT_URI, Channel.PROJECTION, - null, null, null); + public AsyncChannelQueryTask(Executor executor, ContentResolver contentResolver) { + super( + executor, + contentResolver, + TvContract.Channels.CONTENT_URI, + ChannelImpl.PROJECTION, + null, + null, + null); } @Override protected final Channel fromCursor(Cursor c) { - return Channel.fromCursor(c); + return ChannelImpl.fromCursor(c); } } - /** - * Gets an {@link List} of {@link Program}s from {@link TvContract.Programs#CONTENT_URI}. - */ + /** Gets an {@link List} of {@link Program}s from {@link TvContract.Programs#CONTENT_URI}. */ public abstract static class AsyncProgramQueryTask extends AsyncQueryListTask<Program> { - public AsyncProgramQueryTask(ContentResolver contentResolver) { - super(contentResolver, Programs.CONTENT_URI, Program.PROJECTION, null, null, null); + public AsyncProgramQueryTask(Executor executor, ContentResolver contentResolver) { + super( + executor, + contentResolver, + Programs.CONTENT_URI, + Program.PROJECTION, + null, + null, + null); } - public AsyncProgramQueryTask(ContentResolver contentResolver, Uri uri, String selection, - String[] selectionArgs, String sortOrder, CursorFilter filter) { - super(contentResolver, uri, Program.PROJECTION, selection, selectionArgs, sortOrder, + public AsyncProgramQueryTask( + Executor executor, + ContentResolver contentResolver, + Uri uri, + String selection, + String[] selectionArgs, + String sortOrder, + CursorFilter filter) { + super( + executor, + contentResolver, + uri, + Program.PROJECTION, + selection, + selectionArgs, + sortOrder, filter); } @@ -304,13 +338,12 @@ public abstract class AsyncDbTask<Params, Progress, Result> } } - /** - * Gets an {@link List} of {@link TvContract.RecordedPrograms}s. - */ + /** Gets an {@link List} of {@link TvContract.RecordedPrograms}s. */ public abstract static class AsyncRecordedProgramQueryTask extends AsyncQueryListTask<RecordedProgram> { - public AsyncRecordedProgramQueryTask(ContentResolver contentResolver, Uri uri) { - super(contentResolver, uri, RecordedProgram.PROJECTION, null, null, null); + public AsyncRecordedProgramQueryTask( + Executor executor, ContentResolver contentResolver, Uri uri) { + super(executor, contentResolver, uri, RecordedProgram.PROJECTION, null, null, null); } @Override @@ -319,31 +352,39 @@ public abstract class AsyncDbTask<Params, Progress, Result> } } - /** - * Execute the task on the {@link #DB_EXECUTOR} thread. - */ + /** Execute the task on {@link TvSingletons#getDbExecutor()}. */ @SafeVarargs @MainThread public final void executeOnDbThread(Params... params) { - executeOnExecutor(DB_EXECUTOR, params); + mCalledExecuteOnDbThread = true; + executeOnExecutor(mExecutor, params); } /** * Gets an {@link List} of {@link Program}s for a given channel and period {@link - * TvContract#buildProgramsUriForChannel(long, long, long)}. If the {@code period} is - * {@code null}, then all the programs is queried. + * TvContract#buildProgramsUriForChannel(long, long, long)}. If the {@code period} is {@code + * null}, then all the programs is queried. */ public static class LoadProgramsForChannelTask extends AsyncProgramQueryTask { protected final Range<Long> mPeriod; protected final long mChannelId; - public LoadProgramsForChannelTask(ContentResolver contentResolver, long channelId, + public LoadProgramsForChannelTask( + Executor executor, + ContentResolver contentResolver, + long channelId, @Nullable Range<Long> period) { - super(contentResolver, period == null - ? TvContract.buildProgramsUriForChannel(channelId) - : TvContract.buildProgramsUriForChannel(channelId, period.getLower(), - period.getUpper()), - null, null, null, null); + super( + executor, + contentResolver, + period == null + ? TvContract.buildProgramsUriForChannel(channelId) + : TvContract.buildProgramsUriForChannel( + channelId, period.getLower(), period.getUpper()), + null, + null, + null, + null); mPeriod = period; mChannelId = channelId; } @@ -357,14 +398,19 @@ public abstract class AsyncDbTask<Params, Progress, Result> } } - /** - * Gets a single {@link Program} from {@link TvContract.Programs#CONTENT_URI}. - */ + /** Gets a single {@link Program} from {@link TvContract.Programs#CONTENT_URI}. */ public static class AsyncQueryProgramTask extends AsyncQueryItemTask<Program> { - public AsyncQueryProgramTask(ContentResolver contentResolver, long programId) { - super(contentResolver, TvContract.buildProgramUri(programId), Program.PROJECTION, null, - null, null); + public AsyncQueryProgramTask( + Executor executor, ContentResolver contentResolver, long programId) { + super( + executor, + contentResolver, + TvContract.buildProgramUri(programId), + Program.PROJECTION, + null, + null, + null); } @Override @@ -373,8 +419,6 @@ public abstract class AsyncDbTask<Params, Progress, Result> } } - /** - * An interface which filters the row. - */ - public interface CursorFilter extends Filter<Cursor> { } + /** An interface which filters the row. */ + public interface CursorFilter extends Filter<Cursor> {} } diff --git a/src/com/android/tv/util/CaptionSettings.java b/src/com/android/tv/util/CaptionSettings.java index 3b38905b..6d7e9901 100644 --- a/src/com/android/tv/util/CaptionSettings.java +++ b/src/com/android/tv/util/CaptionSettings.java @@ -18,7 +18,6 @@ package com.android.tv.util; import android.content.Context; import android.view.accessibility.CaptioningManager; - import java.util.Locale; public class CaptionSettings { @@ -32,8 +31,8 @@ public class CaptionSettings { private String mTrackId; public CaptionSettings(Context context) { - mCaptioningManager = (CaptioningManager) context.getSystemService( - Context.CAPTIONING_SERVICE); + mCaptioningManager = + (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); } public final String getSystemLanguage() { @@ -84,16 +83,12 @@ public class CaptionSettings { mLanguage = language; } - /** - * Returns the track ID to be used as an alternative key. - */ + /** Returns the track ID to be used as an alternative key. */ public String getTrackId() { return mTrackId; } - /** - * Sets the track ID to be used as an alternative key. - */ + /** Sets the track ID to be used as an alternative key. */ public void setTrackId(String trackId) { mTrackId = trackId; } diff --git a/src/com/android/tv/util/Clock.java b/src/com/android/tv/util/Clock.java deleted file mode 100644 index c5e96431..00000000 --- a/src/com/android/tv/util/Clock.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * 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.android.tv.util; - -import android.os.SystemClock; - -/** - * An interface through which system clocks can be read. The {@link #SYSTEM} implementation - * must be used for all non-test cases. - */ -public interface Clock { - /** - * Returns the current time in milliseconds since January 1, 1970 00:00:00.0 UTC. - * See {@link System#currentTimeMillis()}. - */ - long currentTimeMillis(); - - /** - * Returns milliseconds since boot, including time spent in sleep. - * - * @see SystemClock#elapsedRealtime() - */ - long elapsedRealtime(); - - /** - * Waits a given number of milliseconds (of uptimeMillis) before returning. - * - * @param ms to sleep before returning, in milliseconds of uptime. - * @see SystemClock#sleep(long) - */ - void sleep(long ms); - - /** - * The default implementation of Clock. - */ - Clock SYSTEM = new Clock() { - @Override - public long currentTimeMillis() { - return System.currentTimeMillis(); - } - - @Override - public long elapsedRealtime() { - return SystemClock.elapsedRealtime(); - } - - @Override - public void sleep(long ms) { - SystemClock.sleep(ms); - } - }; -} diff --git a/src/com/android/tv/util/CompositeComparator.java b/src/com/android/tv/util/CompositeComparator.java index 47cf50fe..ccf4e944 100644 --- a/src/com/android/tv/util/CompositeComparator.java +++ b/src/com/android/tv/util/CompositeComparator.java @@ -18,9 +18,7 @@ package com.android.tv.util; import java.util.Comparator; -/** - * A comparator which runs multiple comparators sequentially. - */ +/** A comparator which runs multiple comparators sequentially. */ public class CompositeComparator<T> implements Comparator<T> { private final Comparator<T>[] mComparators; diff --git a/src/com/android/tv/util/Debug.java b/src/com/android/tv/util/Debug.java deleted file mode 100644 index 67a2683d..00000000 --- a/src/com/android/tv/util/Debug.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * 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.android.tv.util; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -/** - * A class only for help developers. - */ -public class Debug { - /** - * A threshold of start up time, when the start up time of Live TV is more than it, - * a warning will show to the developer. - */ - public static final long TIME_START_UP_DURATION_THRESHOLD = TimeUnit.SECONDS.toMillis(6); - /** - * Tag for measuring start up time of Live TV. - */ - public static final String TAG_START_UP_TIMER = "start_up_timer"; - - /** - * A global map for duration timers. - */ - private final static Map<String, DurationTimer> sTimerMap = new HashMap<>(); - - /** - * Returns the global duration timer by tag. - */ - public static DurationTimer getTimer(String tag) { - if (sTimerMap.get(tag) != null) { - return sTimerMap.get(tag); - } - DurationTimer timer = new DurationTimer(tag, true); - sTimerMap.put(tag, timer); - return timer; - } - - /** - * Removes the global duration timer by tag. - */ - public static DurationTimer removeTimer(String tag) { - return sTimerMap.remove(tag); - } -} diff --git a/src/com/android/tv/util/DurationTimer.java b/src/com/android/tv/util/DurationTimer.java deleted file mode 100644 index 1f057bf6..00000000 --- a/src/com/android/tv/util/DurationTimer.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * 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.android.tv.util; - -import android.os.SystemClock; -import android.util.Log; - -import com.android.tv.common.BuildConfig; - -/** - * Times a duration. - */ -public final class DurationTimer { - private static final String TAG = "DurationTimer"; - public static final long TIME_NOT_SET = -1; - - private long mStartTimeMs = TIME_NOT_SET; - private String mTag = TAG; - private boolean mLogEngOnly; - - public DurationTimer() { } - - public DurationTimer(String tag, boolean logEngOnly) { - mTag = tag; - mLogEngOnly = logEngOnly; - } - - /** - * Returns true if the timer is running. - */ - public boolean isRunning() { - return mStartTimeMs != TIME_NOT_SET; - } - - /** - * Start the timer. - */ - public void start() { - mStartTimeMs = SystemClock.elapsedRealtime(); - } - - /** - * Returns true if timer is started. - */ - public boolean isStarted() { - return mStartTimeMs != TIME_NOT_SET; - } - - /** - * Returns the current duration in milliseconds or {@link #TIME_NOT_SET} if the timer is not - * running. - */ - public long getDuration() { - return isRunning() ? SystemClock.elapsedRealtime() - mStartTimeMs : TIME_NOT_SET; - } - - /** - * Stops the timer and resets its value to {@link #TIME_NOT_SET}. - * - * @return the current duration in milliseconds or {@link #TIME_NOT_SET} if the timer is not - * running. - */ - public long reset() { - long duration = getDuration(); - mStartTimeMs = TIME_NOT_SET; - return duration; - } - - /** - * Adds information and duration time to the log. - */ - public void log(String message) { - if (isRunning() && (!mLogEngOnly || BuildConfig.ENG)) { - Log.i(mTag, message + " : " + getDuration() + "ms"); - } - } -} diff --git a/src/com/android/tv/util/Filter.java b/src/com/android/tv/util/Filter.java index d5b356e4..3e24a496 100644 --- a/src/com/android/tv/util/Filter.java +++ b/src/com/android/tv/util/Filter.java @@ -16,12 +16,8 @@ package com.android.tv.util; -/** - * Interface to decide whether an input is filtered out or not. - */ +/** Interface to decide whether an input is filtered out or not. */ public interface Filter<T> { - /** - * Returns true, if {@code input} is acceptable. - */ + /** Returns true, if {@code input} is acceptable. */ boolean filter(T input); } diff --git a/src/com/android/tv/util/LocationUtils.java b/src/com/android/tv/util/LocationUtils.java deleted file mode 100644 index d5d7bee3..00000000 --- a/src/com/android/tv/util/LocationUtils.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * 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.android.tv.util; - -import android.Manifest; -import android.content.Context; -import android.content.pm.PackageManager; -import android.location.Address; -import android.location.Geocoder; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.text.TextUtils; -import android.util.Log; - -import com.android.tv.tuner.util.PostalCodeUtils; - -import java.io.IOException; -import java.util.List; -import java.util.Locale; - -/** - * A utility class to get the current location. - */ -public class LocationUtils { - private static final String TAG = "LocationUtils"; - private static final boolean DEBUG = false; - - private static Context sApplicationContext; - private static Address sAddress; - private static String sCountry; - private static IOException sError; - - /** - * Checks the current location. - */ - public static synchronized Address getCurrentAddress(Context context) throws IOException, - SecurityException { - if (sAddress != null) { - return sAddress; - } - if (sError != null) { - throw sError; - } - if (sApplicationContext == null) { - sApplicationContext = context.getApplicationContext(); - } - LocationUtilsHelper.startLocationUpdates(); - return null; - } - - /** Returns the current country. */ - @NonNull - public static synchronized String getCurrentCountry(Context context) { - if (sCountry != null) { - return sCountry; - } - if (TextUtils.isEmpty(sCountry)) { - sCountry = context.getResources().getConfiguration().locale.getCountry(); - } - return sCountry; - } - - private static void updateAddress(Location location) { - if (DEBUG) Log.d(TAG, "Updating address with " + location); - if (location == null) { - return; - } - Geocoder geocoder = new Geocoder(sApplicationContext, Locale.getDefault()); - try { - List<Address> addresses = geocoder.getFromLocation( - location.getLatitude(), location.getLongitude(), 1); - if (addresses != null && !addresses.isEmpty()) { - sAddress = addresses.get(0); - if (DEBUG) Log.d(TAG, "Got " + sAddress); - try { - PostalCodeUtils.updatePostalCode(sApplicationContext); - } catch (Exception e) { - // Do nothing - } - } else { - if (DEBUG) Log.d(TAG, "No address returned"); - } - sError = null; - } catch (IOException e) { - Log.w(TAG, "Error in updating address", e); - sError = e; - } - } - - private LocationUtils() { } - - private static class LocationUtilsHelper { - private static final LocationListener LOCATION_LISTENER = new LocationListener() { - @Override - public void onLocationChanged(Location location) { - updateAddress(location); - } - - @Override - public void onStatusChanged(String provider, int status, Bundle extras) { } - - @Override - public void onProviderEnabled(String provider) { } - - @Override - public void onProviderDisabled(String provider) { } - }; - - private static LocationManager sLocationManager; - - public static void startLocationUpdates() { - if (sLocationManager == null) { - sLocationManager = (LocationManager) sApplicationContext.getSystemService( - Context.LOCATION_SERVICE); - try { - sLocationManager.requestLocationUpdates( - LocationManager.NETWORK_PROVIDER, 1000, 10, LOCATION_LISTENER, null); - } catch (SecurityException e) { - // Enables requesting the location updates again. - sLocationManager = null; - throw e; - } - } - } - } -} diff --git a/src/com/android/tv/util/MainThreadExecutor.java b/src/com/android/tv/util/MainThreadExecutor.java index ce8f8ff3..5102ddbd 100644 --- a/src/com/android/tv/util/MainThreadExecutor.java +++ b/src/com/android/tv/util/MainThreadExecutor.java @@ -18,7 +18,6 @@ package com.android.tv.util; import android.os.Handler; import android.os.Looper; - import java.util.List; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.TimeUnit; @@ -26,11 +25,11 @@ import java.util.concurrent.TimeUnit; /** * An executor service that executes its tasks on the main thread. * - * Shutting down this executor is not supported. + * <p>Shutting down this executor is not supported. */ public class MainThreadExecutor extends AbstractExecutorService { - private final static MainThreadExecutor INSTANCE = new MainThreadExecutor(); + private static final MainThreadExecutor INSTANCE = new MainThreadExecutor(); public static MainThreadExecutor getInstance() { return INSTANCE; @@ -47,18 +46,14 @@ public class MainThreadExecutor extends AbstractExecutorService { } } - /** - * Not supported and throws an exception when used. - */ + /** Not supported and throws an exception when used. */ @Override @Deprecated public void shutdown() { throw new UnsupportedOperationException(); } - /** - * Not supported and throws an exception when used. - */ + /** Not supported and throws an exception when used. */ @Override @Deprecated public List<Runnable> shutdownNow() { @@ -75,12 +70,10 @@ public class MainThreadExecutor extends AbstractExecutorService { return false; } - /** - * Not supported and throws an exception when used. - */ + /** Not supported and throws an exception when used. */ @Override @Deprecated public boolean awaitTermination(long l, TimeUnit timeUnit) throws InterruptedException { throw new UnsupportedOperationException(); } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/util/MultiLongSparseArray.java b/src/com/android/tv/util/MultiLongSparseArray.java index 1d5fa80b..a456df91 100644 --- a/src/com/android/tv/util/MultiLongSparseArray.java +++ b/src/com/android/tv/util/MultiLongSparseArray.java @@ -19,7 +19,6 @@ package com.android.tv.util; import android.support.annotation.VisibleForTesting; import android.util.ArraySet; import android.util.LongSparseArray; - import java.util.Collections; import java.util.Set; @@ -29,8 +28,7 @@ import java.util.Set; * <p>This has the same memory and performance trade offs listed in {@link LongSparseArray}. */ public class MultiLongSparseArray<T> { - @VisibleForTesting - static final int DEFAULT_MAX_EMPTIES_KEPT = 4; + @VisibleForTesting static final int DEFAULT_MAX_EMPTIES_KEPT = 4; private final LongSparseArray<Set<T>> mSparseArray; private final Set<T>[] mEmptySets; private int mEmptyIndex = -1; @@ -46,9 +44,8 @@ public class MultiLongSparseArray<T> { } /** - * Adds a mapping from the specified key to the specified value, - * replacing the previous mapping from the specified key if there - * was one. + * Adds a mapping from the specified key to the specified value, replacing the previous mapping + * from the specified key if there was one. */ public void put(long key, T value) { Set<T> values = mSparseArray.get(key); @@ -59,9 +56,7 @@ public class MultiLongSparseArray<T> { values.add(value); } - /** - * Removes the value at the specified index. - */ + /** Removes the value at the specified index. */ public void remove(long key, T value) { Set<T> values = mSparseArray.get(key); if (values != null) { @@ -74,17 +69,15 @@ public class MultiLongSparseArray<T> { } /** - * Gets the set of Objects mapped from the specified key, or an empty set - * if no such mapping has been made. + * Gets the set of Objects mapped from the specified key, or an empty set if no such mapping has + * been made. */ public Iterable<T> get(long key) { Set<T> values = mSparseArray.get(key); return values == null ? Collections.EMPTY_SET : values; } - /** - * Clears cached empty sets. - */ + /** Clears cached empty sets. */ public void clearEmptyCache() { while (mEmptyIndex >= 0) { mEmptySets[mEmptyIndex--] = null; diff --git a/src/com/android/tv/util/NamedThreadFactory.java b/src/com/android/tv/util/NamedThreadFactory.java deleted file mode 100644 index fcdde952..00000000 --- a/src/com/android/tv/util/NamedThreadFactory.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * 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.android.tv.util; - -import android.support.annotation.NonNull; - -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * A thread factory that creates threads with a suffix. - */ -public class NamedThreadFactory implements ThreadFactory { - private final AtomicInteger mCount = new AtomicInteger(0); - private final ThreadFactory mDefaultThreadFactory; - private final String mPrefix; - - public NamedThreadFactory(final String baseName) { - mDefaultThreadFactory = Executors.defaultThreadFactory(); - mPrefix = baseName + "-"; - } - - @Override - public Thread newThread(@NonNull final Runnable runnable) { - final Thread thread = mDefaultThreadFactory.newThread(runnable); - thread.setName(mPrefix + mCount.getAndIncrement()); - return thread; - } - - public boolean namedWithPrefix(Thread thread) { - return thread.getName().startsWith(mPrefix); - } -} diff --git a/src/com/android/tv/util/NetworkTrafficTags.java b/src/com/android/tv/util/NetworkTrafficTags.java deleted file mode 100644 index 2dca613c..00000000 --- a/src/com/android/tv/util/NetworkTrafficTags.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * 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.android.tv.util; - -import android.net.TrafficStats; -import android.support.annotation.NonNull; - -import java.util.concurrent.Executor; - -/** Constants for tagging network traffic in the Live channels app. */ -public final class NetworkTrafficTags { - - public static final int DEFAULT_LIVE_CHANNELS = 1; - public static final int LOGO_FETCHER = 2; - public static final int HDHOMERUN = 3; - public static final int EPG_FETCH = 4; - - /** - * An executor which simply wraps a provided delegate executor, but calls {@link - * TrafficStats#setThreadStatsTag(int)} before executing any task. - */ - public static class TrafficStatsTaggingExecutor implements Executor { - private final Executor delegateExecutor; - private final int tag; - - public TrafficStatsTaggingExecutor(Executor delegateExecutor, int tag) { - this.delegateExecutor = delegateExecutor; - this.tag = tag; - } - - @Override - public void execute(final @NonNull Runnable command) { - // TODO(b/62038127): robolectric does not support lamdas in unbundled apps - delegateExecutor.execute( - new Runnable() { - @Override - public void run() { - TrafficStats.setThreadStatsTag(tag); - try { - command.run(); - } finally { - TrafficStats.clearThreadStatsTag(); - } - } - }); - } - } - - private NetworkTrafficTags() {} -} diff --git a/src/com/android/tv/util/NetworkUtils.java b/src/com/android/tv/util/NetworkUtils.java index ed3ce383..94581d5a 100644 --- a/src/com/android/tv/util/NetworkUtils.java +++ b/src/com/android/tv/util/NetworkUtils.java @@ -20,21 +20,16 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; - import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; -/** - * A utility class to check the connectivity. - */ +/** A utility class to check the connectivity. */ @WorkerThread public class NetworkUtils { private static final String GENERATE_204 = "http://clients3.google.com/generate_204"; - /** - * Checks if the internet connection is available. - */ + /** Checks if the internet connection is available. */ public static boolean isNetworkAvailable(@Nullable ConnectivityManager connectivityManager) { if (connectivityManager == null) { return false; @@ -62,5 +57,5 @@ public class NetworkUtils { return false; } - private NetworkUtils() { } + private NetworkUtils() {} } diff --git a/src/com/android/tv/util/OnboardingUtils.java b/src/com/android/tv/util/OnboardingUtils.java index 49b02b82..3b72e091 100644 --- a/src/com/android/tv/util/OnboardingUtils.java +++ b/src/com/android/tv/util/OnboardingUtils.java @@ -21,9 +21,7 @@ import android.content.Intent; import android.net.Uri; import android.preference.PreferenceManager; -/** - * A utility class related to onboarding experience. - */ +/** A utility class related to onboarding experience. */ public final class OnboardingUtils { private static final String PREF_KEY_IS_FIRST_BOOT = "pref_onbaording_is_first_boot"; private static final String PREF_KEY_ONBOARDING_VERSION_CODE = "pref_onbaording_versionCode"; @@ -31,23 +29,17 @@ public final class OnboardingUtils { private static final String MERCHANT_COLLECTION_URL_STRING = getMerchantCollectionUrl(); - /** - * Intent to show merchant collection in online store. - */ - public static final Intent ONLINE_STORE_INTENT = new Intent(Intent.ACTION_VIEW, - Uri.parse(MERCHANT_COLLECTION_URL_STRING)); + /** Intent to show merchant collection in online store. */ + public static final Intent ONLINE_STORE_INTENT = + new Intent(Intent.ACTION_VIEW, Uri.parse(MERCHANT_COLLECTION_URL_STRING)); - /** - * Checks if this is the first boot after the onboarding experience has been applied. - */ + /** Checks if this is the first boot after the onboarding experience has been applied. */ public static boolean isFirstBoot(Context context) { return PreferenceManager.getDefaultSharedPreferences(context) .getBoolean(PREF_KEY_IS_FIRST_BOOT, true); } - /** - * Marks that the first boot has been completed. - */ + /** Marks that the first boot has been completed. */ public static void setFirstBootCompleted(Context context) { PreferenceManager.getDefaultSharedPreferences(context) .edit() @@ -56,27 +48,28 @@ public final class OnboardingUtils { } /** - * Checks if this is the first run of {@link com.android.tv.MainActivity} with the - * current onboarding version. + * Checks if this is the first run of {@link com.android.tv.MainActivity} with the current + * onboarding version. */ public static boolean isFirstRunWithCurrentVersion(Context context) { - int versionCode = PreferenceManager.getDefaultSharedPreferences(context) - .getInt(PREF_KEY_ONBOARDING_VERSION_CODE, 0); + int versionCode = + PreferenceManager.getDefaultSharedPreferences(context) + .getInt(PREF_KEY_ONBOARDING_VERSION_CODE, 0); return versionCode != ONBOARDING_VERSION; } /** - * Marks that the first run of {@link com.android.tv.MainActivity} with the current - * onboarding version has been completed. + * Marks that the first run of {@link com.android.tv.MainActivity} with the current onboarding + * version has been completed. */ public static void setFirstRunWithCurrentVersionCompleted(Context context) { - PreferenceManager.getDefaultSharedPreferences(context).edit() - .putInt(PREF_KEY_ONBOARDING_VERSION_CODE, ONBOARDING_VERSION).apply(); + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putInt(PREF_KEY_ONBOARDING_VERSION_CODE, ONBOARDING_VERSION) + .apply(); } - /** - * Returns merchant collection URL. - */ + /** Returns merchant collection URL. */ private static String getMerchantCollectionUrl() { return "TODO: add a merchant collection url"; } diff --git a/src/com/android/tv/util/Partner.java b/src/com/android/tv/util/Partner.java index e3688392..c5e9aad2 100644 --- a/src/com/android/tv/util/Partner.java +++ b/src/com/android/tv/util/Partner.java @@ -26,7 +26,6 @@ import android.content.res.Resources; import android.media.tv.TvInputInfo; import android.text.TextUtils; import android.util.Log; - import java.util.HashMap; import java.util.Map; @@ -42,6 +41,7 @@ public class Partner { /** ID tags for device input types */ public static final String INPUT_TYPE_BUNDLED_TUNER = "input_type_combined_tuners"; + public static final String INPUT_TYPE_TUNER = "input_type_tuner"; public static final String INPUT_TYPE_CEC_LOGICAL = "input_type_cec_logical"; public static final String INPUT_TYPE_CEC_RECORDER = "input_type_cec_recorder"; @@ -68,6 +68,7 @@ public class Partner { private final Resources mResources; private static final Map<String, Integer> INPUT_TYPE_MAP = new HashMap<>(); + static { INPUT_TYPE_MAP.put(INPUT_TYPE_BUNDLED_TUNER, TvInputManagerHelper.TYPE_BUNDLED_TUNER); INPUT_TYPE_MAP.put(INPUT_TYPE_TUNER, TvInputInfo.TYPE_TUNER); diff --git a/src/com/android/tv/util/PermissionUtils.java b/src/com/android/tv/util/PermissionUtils.java deleted file mode 100644 index a355be99..00000000 --- a/src/com/android/tv/util/PermissionUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.android.tv.util; - -import android.content.Context; -import android.content.pm.PackageManager; - -/** - * Util class to handle permissions. - */ -public class PermissionUtils { - /** - * Permission to read the TV listings. - */ - public static final String PERMISSION_READ_TV_LISTINGS = "android.permission.READ_TV_LISTINGS"; - - private static Boolean sHasAccessAllEpgPermission; - private static Boolean sHasAccessWatchedHistoryPermission; - private static Boolean sHasModifyParentalControlsPermission; - - public static boolean hasAccessAllEpg(Context context) { - if (sHasAccessAllEpgPermission == null) { - sHasAccessAllEpgPermission = context.checkSelfPermission( - "com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA") - == PackageManager.PERMISSION_GRANTED; - } - return sHasAccessAllEpgPermission; - } - - public static boolean hasAccessWatchedHistory(Context context) { - if (sHasAccessWatchedHistoryPermission == null) { - sHasAccessWatchedHistoryPermission = context.checkSelfPermission( - "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") - == PackageManager.PERMISSION_GRANTED; - } - return sHasAccessWatchedHistoryPermission; - } - - public static boolean hasModifyParentalControls(Context context) { - if (sHasModifyParentalControlsPermission == null) { - sHasModifyParentalControlsPermission = context.checkSelfPermission( - "android.permission.MODIFY_PARENTAL_CONTROLS") - == PackageManager.PERMISSION_GRANTED; - } - return sHasModifyParentalControlsPermission; - } - - public static boolean hasReadTvListings(Context context) { - return context.checkSelfPermission(PERMISSION_READ_TV_LISTINGS) - == PackageManager.PERMISSION_GRANTED; - } - - public static boolean hasInternet(Context context) { - return context.checkSelfPermission("android.permission.INTERNET") - == PackageManager.PERMISSION_GRANTED; - } -} diff --git a/src/com/android/tv/util/RecurringRunner.java b/src/com/android/tv/util/RecurringRunner.java index 8b45131b..764689c2 100644 --- a/src/com/android/tv/util/RecurringRunner.java +++ b/src/com/android/tv/util/RecurringRunner.java @@ -22,10 +22,8 @@ import android.os.AsyncTask; import android.os.Handler; import android.support.annotation.WorkerThread; import android.util.Log; - -import com.android.tv.common.SharedPreferencesUtils; import com.android.tv.common.SoftPreconditions; - +import com.android.tv.common.util.SharedPreferencesUtils; import java.util.Date; /** @@ -46,8 +44,8 @@ public final class RecurringRunner { private final String mName; private boolean mRunning; - public RecurringRunner(Context context, long intervalMs, Runnable runnable, - Runnable onStopRunnable) { + public RecurringRunner( + Context context, long intervalMs, Runnable runnable, Runnable onStopRunnable) { mContext = context.getApplicationContext(); mRunnable = runnable; mOnStopRunnable = onStopRunnable; @@ -99,18 +97,21 @@ public final class RecurringRunner { // Run it anyways even if it is in the past if (DEBUG) Log.i(TAG, "Next run of " + mName + " at " + new Date(next)); long delay = Math.max(next - now, 0); - boolean posted = mHandler.postDelayed(new Runnable() { - @Override - public void run() { - try { - if (DEBUG) Log.i(TAG, "Starting " + mName); - mRunnable.run(); - } catch (Exception e) { - Log.w(TAG, "Error running " + mName, e); - } - postAt(resetNextRunTime()); - } - }, delay); + boolean posted = + mHandler.postDelayed( + new Runnable() { + @Override + public void run() { + try { + if (DEBUG) Log.i(TAG, "Starting " + mName); + mRunnable.run(); + } catch (Exception e) { + Log.w(TAG, "Error running " + mName, e); + } + postAt(resetNextRunTime()); + } + }, + delay); if (!posted) { Log.w(TAG, "Scheduling a future run of " + mName + " at " + new Date(next) + "failed"); } @@ -118,8 +119,8 @@ public final class RecurringRunner { } private SharedPreferences getSharedPreferences() { - return mContext.getSharedPreferences(SharedPreferencesUtils.SHARED_PREF_RECURRING_RUNNER, - Context.MODE_PRIVATE); + return mContext.getSharedPreferences( + SharedPreferencesUtils.SHARED_PREF_RECURRING_RUNNER, Context.MODE_PRIVATE); } @WorkerThread diff --git a/src/com/android/tv/util/SetupUtils.java b/src/com/android/tv/util/SetupUtils.java index 32e3a81f..0d536320 100644 --- a/src/com/android/tv/util/SetupUtils.java +++ b/src/com/android/tv/util/SetupUtils.java @@ -28,24 +28,20 @@ import android.media.tv.TvInputManager; import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.support.annotation.UiThread; +import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; - -import com.android.tv.ApplicationSingletons; -import com.android.tv.TvApplication; +import com.android.tv.TvSingletons; +import com.android.tv.common.BaseApplication; import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; import com.android.tv.data.ChannelDataManager; -import com.android.tv.tuner.tvinput.TunerTvInputService; - +import com.android.tv.data.api.Channel; import java.util.Collections; import java.util.HashSet; import java.util.Set; -/** - * A utility class related to input setup. - */ +/** A utility class related to input setup. */ public class SetupUtils { private static final String TAG = "SetupUtils"; private static final boolean DEBUG = false; @@ -58,9 +54,8 @@ public class SetupUtils { // Recognized inputs means that the user already knows the inputs are installed. private static final String PREF_KEY_RECOGNIZED_INPUTS = "recognized_inputs"; private static final String PREF_KEY_IS_FIRST_TUNE = "is_first_tune"; - private static SetupUtils sSetupUtils; - private final TvApplication mTvApplication; + private final Context mContext; private final SharedPreferences mSharedPreferences; private final Set<String> mKnownInputs; private final Set<String> mSetUpInputs; @@ -68,106 +63,104 @@ public class SetupUtils { private boolean mIsFirstTune; private final String mTunerInputId; - private SetupUtils(TvApplication tvApplication) { - mTvApplication = tvApplication; - mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(tvApplication); + @VisibleForTesting + protected SetupUtils(Context context) { + mContext = context; + mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); mSetUpInputs = new ArraySet<>(); - mSetUpInputs.addAll(mSharedPreferences.getStringSet(PREF_KEY_SET_UP_INPUTS, - Collections.emptySet())); + mSetUpInputs.addAll( + mSharedPreferences.getStringSet(PREF_KEY_SET_UP_INPUTS, Collections.emptySet())); mKnownInputs = new ArraySet<>(); - mKnownInputs.addAll(mSharedPreferences.getStringSet(PREF_KEY_KNOWN_INPUTS, - Collections.emptySet())); + mKnownInputs.addAll( + mSharedPreferences.getStringSet(PREF_KEY_KNOWN_INPUTS, Collections.emptySet())); mRecognizedInputs = new ArraySet<>(); - mRecognizedInputs.addAll(mSharedPreferences.getStringSet(PREF_KEY_RECOGNIZED_INPUTS, - mKnownInputs)); + mRecognizedInputs.addAll( + mSharedPreferences.getStringSet(PREF_KEY_RECOGNIZED_INPUTS, mKnownInputs)); mIsFirstTune = mSharedPreferences.getBoolean(PREF_KEY_IS_FIRST_TUNE, true); - mTunerInputId = TvContract.buildInputId(new ComponentName(tvApplication, - TunerTvInputService.class)); + mTunerInputId = BaseApplication.getSingletons(context).getEmbeddedTunerInputId(); } /** - * Gets an instance of {@link SetupUtils}. + * Creates an instance of {@link SetupUtils}. + * + * <p><b>WARNING</b> this should only be called by the top level application. */ - public static SetupUtils getInstance(Context context) { - if (sSetupUtils != null) { - return sSetupUtils; - } - sSetupUtils = new SetupUtils((TvApplication) context.getApplicationContext()); - return sSetupUtils; + public static SetupUtils createForTvSingletons(Context context) { + return new SetupUtils(context.getApplicationContext()); } - /** - * Additional work after the setup of TV input. - */ - public void onTvInputSetupFinished(final String inputId, - @Nullable final Runnable postRunnable) { + /** Additional work after the setup of TV input. */ + public void onTvInputSetupFinished( + final String inputId, @Nullable final Runnable postRunnable) { // When TIS adds several channels, ChannelDataManager.Listener.onChannelList // Updated() can be called several times. In this case, it is hard to detect // which one is the last callback. To reduce error prune, we update channel // list again and make all channels of {@code inputId} browsable. onSetupDone(inputId); - final ChannelDataManager manager = mTvApplication.getChannelDataManager(); + final ChannelDataManager manager = + TvSingletons.getSingletons(mContext).getChannelDataManager(); if (!manager.isDbLoadFinished()) { - manager.addListener(new ChannelDataManager.Listener() { - @Override - public void onLoadFinished() { - manager.removeListener(this); - updateChannelsAfterSetup(mTvApplication, inputId, postRunnable); - } + manager.addListener( + new ChannelDataManager.Listener() { + @Override + public void onLoadFinished() { + manager.removeListener(this); + updateChannelsAfterSetup(mContext, inputId, postRunnable); + } - @Override - public void onChannelListUpdated() { } + @Override + public void onChannelListUpdated() {} - @Override - public void onChannelBrowsableChanged() { } - }); + @Override + public void onChannelBrowsableChanged() {} + }); } else { - updateChannelsAfterSetup(mTvApplication, inputId, postRunnable); + updateChannelsAfterSetup(mContext, inputId, postRunnable); } } - private static void updateChannelsAfterSetup(Context context, final String inputId, - final Runnable postRunnable) { - ApplicationSingletons appSingletons = TvApplication.getSingletons(context); - final ChannelDataManager manager = appSingletons.getChannelDataManager(); - manager.updateChannels(new Runnable() { - @Override - public void run() { - Channel firstChannelForInput = null; - boolean browsableChanged = false; - for (Channel channel : manager.getChannelList()) { - if (channel.getInputId().equals(inputId)) { - if (!channel.isBrowsable()) { - manager.updateBrowsable(channel.getId(), true, true); - browsableChanged = true; + private static void updateChannelsAfterSetup( + Context context, final String inputId, final Runnable postRunnable) { + TvSingletons tvSingletons = TvSingletons.getSingletons(context); + final ChannelDataManager manager = tvSingletons.getChannelDataManager(); + manager.updateChannels( + new Runnable() { + @Override + public void run() { + Channel firstChannelForInput = null; + boolean browsableChanged = false; + for (Channel channel : manager.getChannelList()) { + if (channel.getInputId().equals(inputId)) { + if (!channel.isBrowsable()) { + manager.updateBrowsable(channel.getId(), true, true); + browsableChanged = true; + } + if (firstChannelForInput == null) { + firstChannelForInput = channel; + } + } + } + if (firstChannelForInput != null) { + Utils.setLastWatchedChannel(context, firstChannelForInput); } - if (firstChannelForInput == null) { - firstChannelForInput = channel; + if (browsableChanged) { + manager.notifyChannelBrowsableChanged(); + manager.applyUpdatedValuesToDb(); + } + if (postRunnable != null) { + postRunnable.run(); } } - } - if (firstChannelForInput != null) { - Utils.setLastWatchedChannel(context, firstChannelForInput); - } - if (browsableChanged) { - manager.notifyChannelBrowsableChanged(); - manager.applyUpdatedValuesToDb(); - } - if (postRunnable != null) { - postRunnable.run(); - } - } - }); + }); } - /** - * Marks the channels in newly installed inputs browsable. - */ + /** Marks the channels in newly installed inputs browsable. */ @UiThread public void markNewChannelsBrowsable() { Set<String> newInputsWithChannels = new HashSet<>(); - TvInputManagerHelper tvInputManagerHelper = mTvApplication.getTvInputManagerHelper(); - ChannelDataManager channelDataManager = mTvApplication.getChannelDataManager(); + TvSingletons singletons = TvSingletons.getSingletons(mContext); + TvInputManagerHelper tvInputManagerHelper = singletons.getTvInputManagerHelper(); + ChannelDataManager channelDataManager = singletons.getChannelDataManager(); SoftPreconditions.checkState(channelDataManager.isDbLoadFinished()); for (TvInputInfo input : tvInputManagerHelper.getTvInputInfos(true, true)) { String inputId = input.getId(); @@ -175,9 +168,13 @@ public class SetupUtils { onSetupDone(inputId); newInputsWithChannels.add(inputId); if (DEBUG) { - Log.d(TAG, "New input " + inputId + " has " - + channelDataManager.getChannelCountForInput(inputId) - + " channels"); + Log.d( + TAG, + "New input " + + inputId + + " has " + + channelDataManager.getChannelCountForInput(inputId) + + " channels"); } } } @@ -195,9 +192,7 @@ public class SetupUtils { return mIsFirstTune; } - /** - * Returns true, if the input with {@code inputId} is newly installed. - */ + /** Returns true, if the input with {@code inputId} is newly installed. */ public boolean isNewInput(String inputId) { return !mKnownInputs.contains(inputId); } @@ -209,13 +204,14 @@ public class SetupUtils { public void markAsKnownInput(String inputId) { mKnownInputs.add(inputId); mRecognizedInputs.add(inputId); - mSharedPreferences.edit().putStringSet(PREF_KEY_KNOWN_INPUTS, mKnownInputs) - .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs).apply(); + mSharedPreferences + .edit() + .putStringSet(PREF_KEY_KNOWN_INPUTS, mKnownInputs) + .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs) + .apply(); } - /** - * Returns {@code true}, if {@code inputId}'s setup has been done before. - */ + /** Returns {@code true}, if {@code inputId}'s setup has been done before. */ public boolean isSetupDone(String inputId) { boolean done = mSetUpInputs.contains(inputId); if (DEBUG) { @@ -224,9 +220,7 @@ public class SetupUtils { return done; } - /** - * Returns true, if there is any newly installed input. - */ + /** Returns true, if there is any newly installed input. */ public boolean hasNewInput(TvInputManagerHelper inputManager) { for (TvInputInfo input : inputManager.getTvInputInfos(true, true)) { if (isNewInput(input.getId())) { @@ -236,9 +230,7 @@ public class SetupUtils { return false; } - /** - * Checks whether the given input is already recognized by the user or not. - */ + /** Checks whether the given input is already recognized by the user or not. */ private boolean isRecognizedInput(String inputId) { return mRecognizedInputs.contains(inputId); } @@ -251,13 +243,13 @@ public class SetupUtils { for (TvInputInfo input : inputManager.getTvInputInfos(true, true)) { mRecognizedInputs.add(input.getId()); } - mSharedPreferences.edit().putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs) + mSharedPreferences + .edit() + .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs) .apply(); } - /** - * Checks whether there are any unrecognized inputs. - */ + /** Checks whether there are any unrecognized inputs. */ public boolean hasUnrecognizedInput(TvInputManagerHelper inputManager) { for (TvInputInfo input : inputManager.getTvInputInfos(true, true)) { if (!isRecognizedInput(input.getId())) { @@ -276,8 +268,8 @@ public class SetupUtils { // Find all already-verified packages. Set<String> setUpPackages = new HashSet<>(); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); - for (String input : sp.getStringSet(PREF_KEY_SET_UP_INPUTS, - Collections.<String>emptySet())) { + for (String input : + sp.getStringSet(PREF_KEY_SET_UP_INPUTS, Collections.<String>emptySet())) { if (!TextUtils.isEmpty(input)) { ComponentName componentName = ComponentName.unflattenFromString(input); if (componentName != null) { @@ -299,23 +291,28 @@ public class SetupUtils { */ public static void grantEpgPermission(Context context, String packageName) { if (DEBUG) { - Log.d(TAG, "grantEpgPermission(context=" + context + ", packageName=" + packageName - + ")"); + Log.d( + TAG, + "grantEpgPermission(context=" + context + ", packageName=" + packageName + ")"); } try { - int modeFlags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + int modeFlags = + Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; context.grantUriPermission(packageName, TvContract.Channels.CONTENT_URI, modeFlags); context.grantUriPermission(packageName, TvContract.Programs.CONTENT_URI, modeFlags); } catch (SecurityException e) { - Log.e(TAG, "Either TvProvider does not allow granting of Uri permissions or the app" - + " does not have permission.", e); + Log.e( + TAG, + "Either TvProvider does not allow granting of Uri permissions or the app" + + " does not have permission.", + e); } } /** - * Called when Live channels app is launched. Once it is called, {@link - * #isFirstTune} will return false. + * Called when Live channels app is launched. Once it is called, {@link #isFirstTune} will + * return false. */ public void onTuned() { if (!mIsFirstTune) { @@ -325,9 +322,7 @@ public class SetupUtils { mSharedPreferences.edit().putBoolean(PREF_KEY_IS_FIRST_TUNE, false).apply(); } - /** - * Called when input list is changed. It mainly handles input removals. - */ + /** Called when input list is changed. It mainly handles input removals. */ public void onInputListUpdated(TvInputManager manager) { // mRecognizedInputs > mKnownInputs > mSetUpInputs. Set<String> removedInputList = new HashSet<>(mRecognizedInputs); @@ -345,9 +340,10 @@ public class SetupUtils { try { // Just after booting, input list from TvInputManager are not reliable. // So we need to double-check package existence. b/29034900 - mTvApplication.getPackageManager().getPackageInfo( - ComponentName.unflattenFromString(input) - .getPackageName(), PackageManager.GET_ACTIVITIES); + mContext.getPackageManager() + .getPackageInfo( + ComponentName.unflattenFromString(input).getPackageName(), + PackageManager.GET_ACTIVITIES); Log.i(TAG, "TV input (" + input + ") is removed but package is not deleted"); } catch (NameNotFoundException e) { Log.i(TAG, "TV input (" + input + ") and its package are removed"); @@ -358,9 +354,12 @@ public class SetupUtils { } } if (inputPackageDeleted) { - mSharedPreferences.edit().putStringSet(PREF_KEY_SET_UP_INPUTS, mSetUpInputs) + mSharedPreferences + .edit() + .putStringSet(PREF_KEY_SET_UP_INPUTS, mSetUpInputs) .putStringSet(PREF_KEY_KNOWN_INPUTS, mKnownInputs) - .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs).apply(); + .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs) + .apply(); } } } @@ -375,7 +374,9 @@ public class SetupUtils { if (!mRecognizedInputs.contains(inputId)) { Log.i(TAG, "An unrecognized input's setup has been done. inputId=" + inputId); mRecognizedInputs.add(inputId); - mSharedPreferences.edit().putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs) + mSharedPreferences + .edit() + .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs) .apply(); } if (!mKnownInputs.contains(inputId)) { @@ -388,4 +389,4 @@ public class SetupUtils { mSharedPreferences.edit().putStringSet(PREF_KEY_SET_UP_INPUTS, mSetUpInputs).apply(); } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/util/SqlParams.java b/src/com/android/tv/util/SqlParams.java new file mode 100644 index 00000000..c4b803b6 --- /dev/null +++ b/src/com/android/tv/util/SqlParams.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * 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.android.tv.util; + +import android.database.DatabaseUtils; +import java.util.Arrays; + +/** Convenience class for SQL operations. */ +public class SqlParams { + private String mTables; + private String mSelection; + private String[] mSelectionArgs; + + public SqlParams(String tables, String selection, String... selectionArgs) { + setTables(tables); + setWhere(selection, selectionArgs); + } + + public String getTables() { + return mTables; + } + + public String getSelection() { + return mSelection; + } + + public String[] getSelectionArgs() { + return mSelectionArgs; + } + + public void setTables(String tables) { + mTables = tables; + } + + public void setWhere(String selection, String... selectionArgs) { + mSelection = selection; + mSelectionArgs = selectionArgs; + } + + public void appendWhere(String selection, String... selectionArgs) { + mSelection = DatabaseUtils.concatenateWhere(mSelection, selection); + if (selectionArgs != null) { + mSelectionArgs = DatabaseUtils.appendSelectionArgs(mSelectionArgs, selectionArgs); + } + } + + public void appendWhereEquals(String name, String value) { + appendWhere(name + "=?", value); + } + + @Override + public String toString() { + return "tables " + + getTables() + + " where " + + getSelection() + + " with " + + Arrays.toString(getSelectionArgs()); + } +} diff --git a/src/com/android/tv/util/StringUtils.java b/src/com/android/tv/util/StringUtils.java deleted file mode 100644 index 659807e2..00000000 --- a/src/com/android/tv/util/StringUtils.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * 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.android.tv.util; - -/** - * Utility class for handling {@link String}. - */ -public final class StringUtils { - - private StringUtils() { } - - /** - * Returns compares two strings lexicographically and handles null values quietly. - */ - public static int compare(String a, String b) { - if (a == null) { - return b == null ? 0 : -1; - } - if (b == null) { - return 1; - } - return a.compareTo(b); - } -} diff --git a/src/com/android/tv/util/SystemProperties.java b/src/com/android/tv/util/SystemProperties.java deleted file mode 100644 index e737f233..00000000 --- a/src/com/android/tv/util/SystemProperties.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * 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.android.tv.util; - -import com.android.tv.common.BooleanSystemProperty; - -/** - * A convenience class for getting TV related system properties. - */ -public final class SystemProperties { - - /** - * Allow Google Analytics for eng builds. - */ - public static final BooleanSystemProperty ALLOW_ANALYTICS_IN_ENG = new BooleanSystemProperty( - "tv_allow_analytics_in_eng", false); - - /** - * Allow Strict mode for debug builds. - */ - public static final BooleanSystemProperty ALLOW_STRICT_MODE = new BooleanSystemProperty( - "tv_allow_strict_mode", true); - - /** - * When true {@link android.view.KeyEvent}s are logged. Defaults to false. - */ - public static final BooleanSystemProperty LOG_KEYEVENT = new BooleanSystemProperty( - "tv_log_keyevent", false); - /** - * When true debug keys are used. Defaults to false. - */ - public static final BooleanSystemProperty USE_DEBUG_KEYS = new BooleanSystemProperty( - "tv_use_debug_keys", false); - - /** - * Send {@link com.android.tv.analytics.Tracker} information. Defaults to {@code true}. - */ - public static final BooleanSystemProperty USE_TRACKER = new BooleanSystemProperty( - "tv_use_tracker", true); - - static { - updateSystemProperties(); - } - - private SystemProperties() { - } - - /** - * Update the TV related system properties. - */ - public static void updateSystemProperties() { - BooleanSystemProperty.resetAll(); - } -} diff --git a/src/com/android/tv/util/TimeShiftUtils.java b/src/com/android/tv/util/TimeShiftUtils.java index 8038a78f..977f3333 100644 --- a/src/com/android/tv/util/TimeShiftUtils.java +++ b/src/com/android/tv/util/TimeShiftUtils.java @@ -18,9 +18,7 @@ package com.android.tv.util; import java.util.concurrent.TimeUnit; -/** - * A class that includes convenience methods for time shift plays. - */ +/** A class that includes convenience methods for time shift plays. */ public class TimeShiftUtils { private static final String TAG = "TimeShiftUtils"; private static final boolean DEBUG = false; @@ -30,8 +28,8 @@ public class TimeShiftUtils { private static final int[] LONG_PROGRAM_SPEED_FACTORS = new int[] {2, 8, 32, 128}; /** - * The maximum play speed level support by time shift play. In other words, the valid - * speed levels are ranged from 0 to MAX_SPEED_LEVEL (included). + * The maximum play speed level support by time shift play. In other words, the valid speed + * levels are ranged from 0 to MAX_SPEED_LEVEL (included). */ public static final int MAX_SPEED_LEVEL = SHORT_PROGRAM_SPEED_FACTORS.length - 1; @@ -45,17 +43,19 @@ public class TimeShiftUtils { */ public static int getPlaybackSpeed(int speedLevel, long programDurationMillis) throws IndexOutOfBoundsException { - return (programDurationMillis > SHORT_PROGRAM_THRESHOLD_MILLIS) ? - LONG_PROGRAM_SPEED_FACTORS[speedLevel] : SHORT_PROGRAM_SPEED_FACTORS[speedLevel]; + return (programDurationMillis > SHORT_PROGRAM_THRESHOLD_MILLIS) + ? LONG_PROGRAM_SPEED_FACTORS[speedLevel] + : SHORT_PROGRAM_SPEED_FACTORS[speedLevel]; } /** * Returns the maxium possible play speed according to the program's length. + * * @param programDurationMillis the length of program under playing. */ public static int getMaxPlaybackSpeed(long programDurationMillis) { - return (programDurationMillis > SHORT_PROGRAM_THRESHOLD_MILLIS) ? - LONG_PROGRAM_SPEED_FACTORS[MAX_SPEED_LEVEL] + return (programDurationMillis > SHORT_PROGRAM_THRESHOLD_MILLIS) + ? LONG_PROGRAM_SPEED_FACTORS[MAX_SPEED_LEVEL] : SHORT_PROGRAM_SPEED_FACTORS[MAX_SPEED_LEVEL]; } } diff --git a/src/com/android/tv/util/ToastUtils.java b/src/com/android/tv/util/ToastUtils.java index 34346b2a..a25653f6 100644 --- a/src/com/android/tv/util/ToastUtils.java +++ b/src/com/android/tv/util/ToastUtils.java @@ -19,18 +19,13 @@ package com.android.tv.util; import android.content.Context; import android.support.annotation.MainThread; import android.widget.Toast; - import java.lang.ref.WeakReference; -/** - * A utility class for the toast message. - */ +/** A utility class for the toast message. */ public class ToastUtils { private static WeakReference<Toast> sToast; - /** - * Shows the toast message after canceling the previous one. - */ + /** Shows the toast message after canceling the previous one. */ @MainThread public static void show(Context context, CharSequence text, int duration) { if (sToast != null && sToast.get() != null) { diff --git a/src/com/android/tv/util/TvInputManagerHelper.java b/src/com/android/tv/util/TvInputManagerHelper.java index 730a985b..625fb7b2 100644 --- a/src/com/android/tv/util/TvInputManagerHelper.java +++ b/src/com/android/tv/util/TvInputManagerHelper.java @@ -21,21 +21,23 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.hardware.hdmi.HdmiDeviceInfo; +import android.media.tv.TvContentRatingSystemInfo; import android.media.tv.TvInputInfo; import android.media.tv.TvInputManager; import android.media.tv.TvInputManager.TvInputCallback; import android.os.Handler; +import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; - -import com.android.tv.Features; +import com.android.tv.TvFeatures; import com.android.tv.common.SoftPreconditions; -import com.android.tv.common.TvCommonUtils; +import com.android.tv.common.util.CommonUtils; import com.android.tv.parental.ContentRatingsManager; import com.android.tv.parental.ParentalControlSettings; - +import com.android.tv.util.images.ImageCache; +import com.android.tv.util.images.ImageLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -49,10 +51,61 @@ public class TvInputManagerHelper { private static final String TAG = "TvInputManagerHelper"; private static final boolean DEBUG = false; - /** - * Types of HDMI device and bundled tuner. - */ + public interface TvInputManagerInterface { + TvInputInfo getTvInputInfo(String inputId); + + Integer getInputState(String inputId); + + void registerCallback(TvInputCallback internalCallback, Handler handler); + + void unregisterCallback(TvInputCallback internalCallback); + + List<TvInputInfo> getTvInputList(); + + List<TvContentRatingSystemInfo> getTvContentRatingSystemList(); + } + + private static final class TvInputManagerImpl implements TvInputManagerInterface { + private final TvInputManager delegate; + + private TvInputManagerImpl(TvInputManager delegate) { + this.delegate = delegate; + } + + @Override + public TvInputInfo getTvInputInfo(String inputId) { + return delegate.getTvInputInfo(inputId); + } + + @Override + public Integer getInputState(String inputId) { + return delegate.getInputState(inputId); + } + + @Override + public void registerCallback(TvInputCallback internalCallback, Handler handler) { + delegate.registerCallback(internalCallback, handler); + } + + @Override + public void unregisterCallback(TvInputCallback internalCallback) { + delegate.unregisterCallback(internalCallback); + } + + @Override + public List<TvInputInfo> getTvInputList() { + return delegate.getTvInputList(); + } + + @Override + public List<TvContentRatingSystemInfo> getTvContentRatingSystemList() { + return delegate.getTvContentRatingSystemList(); + } + } + + /** Types of HDMI device and bundled tuner. */ public static final int TYPE_CEC_DEVICE = -2; + public static final int TYPE_BUNDLED_TUNER = -3; public static final int TYPE_CEC_DEVICE_RECORDER = -4; public static final int TYPE_CEC_DEVICE_PLAYBACK = -5; @@ -60,14 +113,13 @@ public class TvInputManagerHelper { private static final String PERMISSION_ACCESS_ALL_EPG_DATA = "com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA"; - private static final String [] mPhysicalTunerBlackList = { + private static final String[] mPhysicalTunerBlackList = { }; private static final String META_LABEL_SORT_KEY = "input_sort_key"; - /** - * The default tv input priority to show. - */ + /** The default tv input priority to show. */ private static final ArrayList<Integer> DEFAULT_TV_INPUT_PRIORITY = new ArrayList<>(); + static { DEFAULT_TV_INPUT_PRIORITY.add(TYPE_BUNDLED_TUNER); DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_TUNER); @@ -90,12 +142,12 @@ public class TvInputManagerHelper { }; private static final String[] TESTABLE_INPUTS = { - "com.android.tv.testinput/.TestTvInputService" + "com.android.tv.testinput/.TestTvInputService" }; private final Context mContext; private final PackageManager mPackageManager; - private final TvInputManager mTvInputManager; + protected final TvInputManagerInterface mTvInputManager; private final Map<String, Integer> mInputStateMap = new HashMap<>(); private final Map<String, TvInputInfo> mInputMap = new HashMap<>(); private final Map<String, String> mTvInputLabels = new ArrayMap<>(); @@ -106,100 +158,105 @@ public class TvInputManagerHelper { private final Map<String, Drawable> mTvInputApplicationIcons = new ArrayMap<>(); private final Map<String, Drawable> mTvInputAppliactionBanners = new ArrayMap<>(); - private final TvInputCallback mInternalCallback = new TvInputCallback() { - @Override - public void onInputStateChanged(String inputId, int state) { - if (DEBUG) Log.d(TAG, "onInputStateChanged " + inputId + " state=" + state); - if (isInBlackList(inputId)) { - return; - } - mInputStateMap.put(inputId, state); - for (TvInputCallback callback : mCallbacks) { - callback.onInputStateChanged(inputId, state); - } - } + private final TvInputCallback mInternalCallback = + new TvInputCallback() { + @Override + public void onInputStateChanged(String inputId, int state) { + if (DEBUG) Log.d(TAG, "onInputStateChanged " + inputId + " state=" + state); + if (isInBlackList(inputId)) { + return; + } + mInputStateMap.put(inputId, state); + for (TvInputCallback callback : mCallbacks) { + callback.onInputStateChanged(inputId, state); + } + } - @Override - public void onInputAdded(String inputId) { - if (DEBUG) Log.d(TAG, "onInputAdded " + inputId); - if (isInBlackList(inputId)) { - return; - } - TvInputInfo info = mTvInputManager.getTvInputInfo(inputId); - if (info != null) { - mInputMap.put(inputId, info); - mTvInputLabels.put(inputId, info.loadLabel(mContext).toString()); - CharSequence inputCustomLabel = info.loadCustomLabel(mContext); - if (inputCustomLabel != null) { - mTvInputCustomLabels.put(inputId, inputCustomLabel.toString()); + @Override + public void onInputAdded(String inputId) { + if (DEBUG) Log.d(TAG, "onInputAdded " + inputId); + if (isInBlackList(inputId)) { + return; + } + TvInputInfo info = mTvInputManager.getTvInputInfo(inputId); + if (info != null) { + mInputMap.put(inputId, info); + CharSequence label = info.loadLabel(mContext); + // in tests the label may be missing just use the input id + mTvInputLabels.put(inputId, label != null ? label.toString() : inputId); + CharSequence inputCustomLabel = info.loadCustomLabel(mContext); + if (inputCustomLabel != null) { + mTvInputCustomLabels.put(inputId, inputCustomLabel.toString()); + } + mInputStateMap.put(inputId, mTvInputManager.getInputState(inputId)); + mInputIdToPartnerInputMap.put(inputId, isPartnerInput(info)); + } + mContentRatingsManager.update(); + for (TvInputCallback callback : mCallbacks) { + callback.onInputAdded(inputId); + } } - mInputStateMap.put(inputId, mTvInputManager.getInputState(inputId)); - mInputIdToPartnerInputMap.put(inputId, isPartnerInput(info)); - } - mContentRatingsManager.update(); - for (TvInputCallback callback : mCallbacks) { - callback.onInputAdded(inputId); - } - } - @Override - public void onInputRemoved(String inputId) { - if (DEBUG) Log.d(TAG, "onInputRemoved " + inputId); - mInputMap.remove(inputId); - mTvInputLabels.remove(inputId); - mTvInputCustomLabels.remove(inputId); - mTvInputApplicationLabels.remove(inputId); - mTvInputApplicationIcons.remove(inputId); - mTvInputAppliactionBanners.remove(inputId); - mInputStateMap.remove(inputId); - mInputIdToPartnerInputMap.remove(inputId); - mContentRatingsManager.update(); - for (TvInputCallback callback : mCallbacks) { - callback.onInputRemoved(inputId); - } - ImageCache.getInstance().remove(ImageLoader.LoadTvInputLogoTask.getTvInputLogoKey( - inputId)); - } + @Override + public void onInputRemoved(String inputId) { + if (DEBUG) Log.d(TAG, "onInputRemoved " + inputId); + mInputMap.remove(inputId); + mTvInputLabels.remove(inputId); + mTvInputCustomLabels.remove(inputId); + mTvInputApplicationLabels.remove(inputId); + mTvInputApplicationIcons.remove(inputId); + mTvInputAppliactionBanners.remove(inputId); + mInputStateMap.remove(inputId); + mInputIdToPartnerInputMap.remove(inputId); + mContentRatingsManager.update(); + for (TvInputCallback callback : mCallbacks) { + callback.onInputRemoved(inputId); + } + ImageCache.getInstance() + .remove(ImageLoader.LoadTvInputLogoTask.getTvInputLogoKey(inputId)); + } - @Override - public void onInputUpdated(String inputId) { - if (DEBUG) Log.d(TAG, "onInputUpdated " + inputId); - if (isInBlackList(inputId)) { - return; - } - TvInputInfo info = mTvInputManager.getTvInputInfo(inputId); - mInputMap.put(inputId, info); - mTvInputLabels.put(inputId, info.loadLabel(mContext).toString()); - CharSequence inputCustomLabel = info.loadCustomLabel(mContext); - if (inputCustomLabel != null) { - mTvInputCustomLabels.put(inputId, inputCustomLabel.toString()); - } - mTvInputApplicationLabels.remove(inputId); - mTvInputApplicationIcons.remove(inputId); - mTvInputAppliactionBanners.remove(inputId); - for (TvInputCallback callback : mCallbacks) { - callback.onInputUpdated(inputId); - } - ImageCache.getInstance().remove(ImageLoader.LoadTvInputLogoTask.getTvInputLogoKey( - inputId)); - } + @Override + public void onInputUpdated(String inputId) { + if (DEBUG) Log.d(TAG, "onInputUpdated " + inputId); + if (isInBlackList(inputId)) { + return; + } + TvInputInfo info = mTvInputManager.getTvInputInfo(inputId); + mInputMap.put(inputId, info); + mTvInputLabels.put(inputId, info.loadLabel(mContext).toString()); + CharSequence inputCustomLabel = info.loadCustomLabel(mContext); + if (inputCustomLabel != null) { + mTvInputCustomLabels.put(inputId, inputCustomLabel.toString()); + } + mTvInputApplicationLabels.remove(inputId); + mTvInputApplicationIcons.remove(inputId); + mTvInputAppliactionBanners.remove(inputId); + for (TvInputCallback callback : mCallbacks) { + callback.onInputUpdated(inputId); + } + ImageCache.getInstance() + .remove(ImageLoader.LoadTvInputLogoTask.getTvInputLogoKey(inputId)); + } - @Override - public void onTvInputInfoUpdated(TvInputInfo inputInfo) { - if (DEBUG) Log.d(TAG, "onTvInputInfoUpdated " + inputInfo); - mInputMap.put(inputInfo.getId(), inputInfo); - mTvInputLabels.put(inputInfo.getId(), inputInfo.loadLabel(mContext).toString()); - CharSequence inputCustomLabel = inputInfo.loadCustomLabel(mContext); - if (inputCustomLabel != null) { - mTvInputCustomLabels.put(inputInfo.getId(), inputCustomLabel.toString()); - } - for (TvInputCallback callback : mCallbacks) { - callback.onTvInputInfoUpdated(inputInfo); - } - ImageCache.getInstance().remove(ImageLoader.LoadTvInputLogoTask.getTvInputLogoKey( - inputInfo.getId())); - } - }; + @Override + public void onTvInputInfoUpdated(TvInputInfo inputInfo) { + if (DEBUG) Log.d(TAG, "onTvInputInfoUpdated " + inputInfo); + mInputMap.put(inputInfo.getId(), inputInfo); + mTvInputLabels.put(inputInfo.getId(), inputInfo.loadLabel(mContext).toString()); + CharSequence inputCustomLabel = inputInfo.loadCustomLabel(mContext); + if (inputCustomLabel != null) { + mTvInputCustomLabels.put(inputInfo.getId(), inputCustomLabel.toString()); + } + for (TvInputCallback callback : mCallbacks) { + callback.onTvInputInfoUpdated(inputInfo); + } + ImageCache.getInstance() + .remove( + ImageLoader.LoadTvInputLogoTask.getTvInputLogoKey( + inputInfo.getId())); + } + }; private final Handler mHandler = new Handler(); private boolean mStarted; @@ -209,10 +266,23 @@ public class TvInputManagerHelper { private final Comparator<TvInputInfo> mTvInputInfoComparator; public TvInputManagerHelper(Context context) { + this(context, createTvInputManagerWrapper(context)); + } + + @Nullable + protected static TvInputManagerImpl createTvInputManagerWrapper(Context context) { + TvInputManager tvInputManager = + (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE); + return tvInputManager == null ? null : new TvInputManagerImpl(tvInputManager); + } + + @VisibleForTesting + protected TvInputManagerHelper( + Context context, @Nullable TvInputManagerInterface tvInputManager) { mContext = context.getApplicationContext(); mPackageManager = context.getPackageManager(); - mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE); - mContentRatingsManager = new ContentRatingsManager(context); + mTvInputManager = tvInputManager; + mContentRatingsManager = new ContentRatingsManager(context, tvInputManager); mParentalControlSettings = new ParentalControlSettings(context); mTvInputInfoComparator = new InputComparatorInternal(this); } @@ -247,7 +317,9 @@ public class TvInputManagerHelper { mInputStateMap.put(inputId, state); mInputIdToPartnerInputMap.put(inputId, isPartnerInput(input)); } - SoftPreconditions.checkState(mInputStateMap.size() == mInputMap.size(), TAG, + SoftPreconditions.checkState( + mInputStateMap.size() == mInputMap.size(), + TAG, "mInputStateMap not the same size as mInputMap"); mContentRatingsManager.update(); } @@ -264,13 +336,12 @@ public class TvInputManagerHelper { mTvInputCustomLabels.clear(); mTvInputApplicationLabels.clear(); mTvInputApplicationIcons.clear(); - mTvInputAppliactionBanners.clear();; + mTvInputAppliactionBanners.clear(); + ; mInputIdToPartnerInputMap.clear(); } - /** - * Clears the TvInput labels map. - */ + /** Clears the TvInput labels map. */ public void clearTvInputLabels() { mTvInputLabels.clear(); mTvInputCustomLabels.clear(); @@ -294,8 +365,8 @@ public class TvInputManagerHelper { } /** - * Returns the default comparator for {@link TvInputInfo}. - * See {@link InputComparatorInternal} for detail. + * Returns the default comparator for {@link TvInputInfo}. See {@link InputComparatorInternal} + * for detail. */ public Comparator<TvInputInfo> getDefaultTvInputInfoComparator() { return mTvInputInfoComparator; @@ -304,35 +375,31 @@ public class TvInputManagerHelper { /** * Checks if the input is from a partner. * - * It's visible for comparator test. - * Package private is enough for this method, but public is necessary to workaround mockito - * bug. + * <p>It's visible for comparator test. Package private is enough for this method, but public is + * necessary to workaround mockito bug. */ @VisibleForTesting public boolean isPartnerInput(TvInputInfo inputInfo) { return isSystemInput(inputInfo) && !isBundledInput(inputInfo); } - /** - * Does the input have {@link ApplicationInfo#FLAG_SYSTEM} set. - */ + /** Does the input have {@link ApplicationInfo#FLAG_SYSTEM} set. */ public boolean isSystemInput(TvInputInfo inputInfo) { return inputInfo != null - && (inputInfo.getServiceInfo().applicationInfo.flags - & ApplicationInfo.FLAG_SYSTEM) != 0; + && (inputInfo.getServiceInfo().applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) + != 0; } - /** - * Is the input one known bundled inputs not written by OEM/SOCs. - */ + /** Is the input one known bundled inputs not written by OEM/SOCs. */ public boolean isBundledInput(TvInputInfo inputInfo) { - return inputInfo != null && Utils.isInBundledPackageSet(inputInfo.getServiceInfo() - .applicationInfo.packageName); + return inputInfo != null + && CommonUtils.isInBundledPackageSet( + inputInfo.getServiceInfo().applicationInfo.packageName); } /** - * Returns if the given input is bundled and written by OEM/SOCs. - * This returns the cached result. + * Returns if the given input is bundled and written by OEM/SOCs. This returns the cached + * result. */ public boolean isPartnerInput(String inputId) { Boolean isPartnerInput = mInputIdToPartnerInputMap.get(inputId); @@ -348,9 +415,7 @@ public class TvInputManagerHelper { return mTvInputManager != null; } - /** - * Loads label of {@code info}. - */ + /** Loads label of {@code info}. */ public String loadLabel(TvInputInfo info) { String label = mTvInputLabels.get(info.getId()); if (label == null) { @@ -360,9 +425,7 @@ public class TvInputManagerHelper { return label; } - /** - * Loads custom label of {@code info} - */ + /** Loads custom label of {@code info} */ public String loadCustomLabel(TvInputInfo info) { String customLabel = mTvInputCustomLabels.get(info.getId()); if (customLabel == null) { @@ -375,60 +438,46 @@ public class TvInputManagerHelper { return customLabel; } - /** - * Gets the tv input application's label. - */ + /** Gets the tv input application's label. */ public CharSequence getTvInputApplicationLabel(CharSequence inputId) { return mTvInputApplicationLabels.get(inputId); } - /** - * Stores the tv input application's label. - */ + /** Stores the tv input application's label. */ public void setTvInputApplicationLabel(String inputId, CharSequence label) { mTvInputApplicationLabels.put(inputId, label); } - /** - * Gets the tv input application's icon. - */ + /** Gets the tv input application's icon. */ public Drawable getTvInputApplicationIcon(String inputId) { return mTvInputApplicationIcons.get(inputId); } - /** - * Stores the tv input application's icon. - */ + /** Stores the tv input application's icon. */ public void setTvInputApplicationIcon(String inputId, Drawable icon) { mTvInputApplicationIcons.put(inputId, icon); } - /** - * Gets the tv input application's banner. - */ + /** Gets the tv input application's banner. */ public Drawable getTvInputApplicationBanner(String inputId) { return mTvInputAppliactionBanners.get(inputId); } - /** - * Stores the tv input application's banner. - */ + /** Stores the tv input application's banner. */ public void setTvInputApplicationBanner(String inputId, Drawable banner) { mTvInputAppliactionBanners.put(inputId, banner); } - /** - * Returns if TV input exists with the input id. - */ + /** Returns if TV input exists with the input id. */ public boolean hasTvInputInfo(String inputId) { - SoftPreconditions.checkState(mStarted, TAG, - "hasTvInputInfo() called before TvInputManagerHelper was started."); + SoftPreconditions.checkState( + mStarted, TAG, "hasTvInputInfo() called before TvInputManagerHelper was started."); return mStarted && !TextUtils.isEmpty(inputId) && mInputMap.get(inputId) != null; } public TvInputInfo getTvInputInfo(String inputId) { - SoftPreconditions.checkState(mStarted, TAG, - "getTvInputInfo() called before TvInputManagerHelper was started."); + SoftPreconditions.checkState( + mStarted, TAG, "getTvInputInfo() called before TvInputManagerHelper was started."); if (!mStarted) { return null; } @@ -452,16 +501,23 @@ public class TvInputManagerHelper { } return size; } - - public int getInputState(TvInputInfo inputInfo) { - return getInputState(inputInfo.getId()); + /** + * Returns TvInputInfo's input state. + * + * @param inputInfo + * @return An Integer which stands for the input state {@link + * TvInputManager.INPUT_STATE_DISCONNECTED} if inputInfo is null + */ + public int getInputState(@Nullable TvInputInfo inputInfo) { + return inputInfo == null + ? TvInputManager.INPUT_STATE_DISCONNECTED + : getInputState(inputInfo.getId()); } public int getInputState(String inputId) { SoftPreconditions.checkState(mStarted, TAG, "AvailabilityManager not started"); if (!mStarted) { return TvInputManager.INPUT_STATE_DISCONNECTED; - } Integer state = mInputStateMap.get(inputId); if (state == null) { @@ -483,16 +539,13 @@ public class TvInputManagerHelper { return mParentalControlSettings; } - /** - * Returns a ContentRatingsManager instance for a given application context. - */ + /** Returns a ContentRatingsManager instance for a given application context. */ public ContentRatingsManager getContentRatingsManager() { return mContentRatingsManager; } private int getInputSortKey(TvInputInfo input) { - return input.getServiceInfo().metaData.getInt(META_LABEL_SORT_KEY, - Integer.MAX_VALUE); + return input.getServiceInfo().metaData.getInt(META_LABEL_SORT_KEY, Integer.MAX_VALUE); } private boolean isInputPhysicalTuner(TvInputInfo input) { @@ -504,15 +557,20 @@ public class TvInputManagerHelper { if (input.createSetupIntent() == null) { return false; } else { - boolean mayBeTunerInput = mPackageManager.checkPermission( - PERMISSION_ACCESS_ALL_EPG_DATA, input.getServiceInfo().packageName) - == PackageManager.PERMISSION_GRANTED; + boolean mayBeTunerInput = + mPackageManager.checkPermission( + PERMISSION_ACCESS_ALL_EPG_DATA, + input.getServiceInfo().packageName) + == PackageManager.PERMISSION_GRANTED; if (!mayBeTunerInput) { try { - ApplicationInfo ai = mPackageManager.getApplicationInfo( - input.getServiceInfo().packageName, 0); - if ((ai.flags & (ApplicationInfo.FLAG_SYSTEM - | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) == 0) { + ApplicationInfo ai = + mPackageManager.getApplicationInfo( + input.getServiceInfo().packageName, 0); + if ((ai.flags + & (ApplicationInfo.FLAG_SYSTEM + | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) + == 0) { return false; } } catch (PackageManager.NameNotFoundException e) { @@ -524,14 +582,15 @@ public class TvInputManagerHelper { } private boolean isInBlackList(String inputId) { - if (Features.USE_PARTNER_INPUT_BLACKLIST.isEnabled(mContext)) { + if (TvFeatures.USE_PARTNER_INPUT_BLACKLIST.isEnabled(mContext)) { for (String disabledTunerInputPrefix : PARTNER_TUNER_INPUT_PREFIX_BLACKLIST) { if (inputId.contains(disabledTunerInputPrefix)) { return true; } } } - if (TvCommonUtils.isRunningInTest()) { + if (CommonUtils.isRoboTest()) return false; + if (CommonUtils.isRunningInTest()) { for (String testableInput : TESTABLE_INPUTS) { if (testableInput.equals(inputId)) { return false; @@ -545,10 +604,9 @@ public class TvInputManagerHelper { /** * Default comparator for TvInputInfo. * - * It's static class that accepts {@link TvInputManagerHelper} as parameter to test. - * To test comparator, we need to mock API in parent class such as {@link #isPartnerInput}, - * but it's impossible for an inner class to use mocked methods. - * (i.e. Mockito's spy doesn't work) + * <p>It's static class that accepts {@link TvInputManagerHelper} as parameter to test. To test + * comparator, we need to mock API in parent class such as {@link #isPartnerInput}, but it's + * impossible for an inner class to use mocked methods. (i.e. Mockito's spy doesn't work) */ @VisibleForTesting static class InputComparatorInternal implements Comparator<TvInputInfo> { @@ -568,8 +626,8 @@ public class TvInputManagerHelper { } /** - * A comparator used for {@link com.android.tv.ui.SelectInputView} to show the list of - * TV inputs. + * A comparator used for {@link com.android.tv.ui.SelectInputView} to show the list of TV + * inputs. */ public static class HardwareInputComparator implements Comparator<TvInputInfo> { private Map<Integer, Integer> mTypePriorities = new HashMap<>(); @@ -591,10 +649,12 @@ public class TvInputManagerHelper { return -1; } - boolean enabledL = (mTvInputManagerHelper.getInputState(lhs) - != TvInputManager.INPUT_STATE_DISCONNECTED); - boolean enabledR = (mTvInputManagerHelper.getInputState(rhs) - != TvInputManager.INPUT_STATE_DISCONNECTED); + boolean enabledL = + (mTvInputManagerHelper.getInputState(lhs) + != TvInputManager.INPUT_STATE_DISCONNECTED); + boolean enabledR = + (mTvInputManagerHelper.getInputState(rhs) + != TvInputManager.INPUT_STATE_DISCONNECTED); if (enabledL != enabledR) { return enabledL ? -1 : 1; } @@ -620,11 +680,13 @@ public class TvInputManagerHelper { return sortKeyR - sortKeyL; } - String parentLabelL = lhs.getParentId() != null - ? getLabel(mTvInputManagerHelper.getTvInputInfo(lhs.getParentId())) + String parentLabelL = + lhs.getParentId() != null + ? getLabel(mTvInputManagerHelper.getTvInputInfo(lhs.getParentId())) : getLabel(mTvInputManagerHelper.getTvInputInfo(lhs.getId())); - String parentLabelR = rhs.getParentId() != null - ? getLabel(mTvInputManagerHelper.getTvInputInfo(rhs.getParentId())) + String parentLabelR = + rhs.getParentId() != null + ? getLabel(mTvInputManagerHelper.getTvInputInfo(rhs.getParentId())) : getLabel(mTvInputManagerHelper.getTvInputInfo(rhs.getId())); if (!TextUtils.equals(parentLabelL, parentLabelR)) { diff --git a/src/com/android/tv/util/TvSettings.java b/src/com/android/tv/util/TvSettings.java index c5fde317..ae79e7e5 100644 --- a/src/com/android/tv/util/TvSettings.java +++ b/src/com/android/tv/util/TvSettings.java @@ -21,7 +21,6 @@ import android.content.SharedPreferences; import android.media.tv.TvTrackInfo; import android.preference.PreferenceManager; import android.support.annotation.IntDef; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collections; @@ -29,11 +28,11 @@ import java.util.HashSet; import java.util.Set; /** - * A class about the constants for TV settings. - * Objects that are returned from the various {@code get} methods must be treated as immutable. + * A class about the constants for TV settings. Objects that are returned from the various {@code + * get} methods must be treated as immutable. */ public final class TvSettings { - public static final String PREF_DISPLAY_MODE = "display_mode"; // int value + public static final String PREF_DISPLAY_MODE = "display_mode"; // int value public static final String PREF_PIN = "pin"; // 4-digit string value. Otherwise, it's not set. // Multi-track audio settings @@ -56,9 +55,14 @@ public final class TvSettings { @Retention(RetentionPolicy.SOURCE) @IntDef({ - CONTENT_RATING_LEVEL_NONE, CONTENT_RATING_LEVEL_HIGH, CONTENT_RATING_LEVEL_MEDIUM, - CONTENT_RATING_LEVEL_LOW, CONTENT_RATING_LEVEL_CUSTOM }) + CONTENT_RATING_LEVEL_NONE, + CONTENT_RATING_LEVEL_HIGH, + CONTENT_RATING_LEVEL_MEDIUM, + CONTENT_RATING_LEVEL_LOW, + CONTENT_RATING_LEVEL_CUSTOM + }) public @interface ContentRatingLevel {} + public static final int CONTENT_RATING_LEVEL_NONE = 0; public static final int CONTENT_RATING_LEVEL_HIGH = 1; public static final int CONTENT_RATING_LEVEL_MEDIUM = 2; @@ -69,61 +73,74 @@ public final class TvSettings { // Multi-track audio settings public static String getMultiAudioId(Context context) { - return PreferenceManager.getDefaultSharedPreferences(context).getString( - PREF_MULTI_AUDIO_ID, null); + return PreferenceManager.getDefaultSharedPreferences(context) + .getString(PREF_MULTI_AUDIO_ID, null); } public static void setMultiAudioId(Context context, String language) { - PreferenceManager.getDefaultSharedPreferences(context).edit().putString( - PREF_MULTI_AUDIO_ID, language).apply(); + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putString(PREF_MULTI_AUDIO_ID, language) + .apply(); } public static String getMultiAudioLanguage(Context context) { - return PreferenceManager.getDefaultSharedPreferences(context).getString( - PREF_MULTI_AUDIO_LANGUAGE, null); + return PreferenceManager.getDefaultSharedPreferences(context) + .getString(PREF_MULTI_AUDIO_LANGUAGE, null); } public static void setMultiAudioLanguage(Context context, String language) { - PreferenceManager.getDefaultSharedPreferences(context).edit().putString( - PREF_MULTI_AUDIO_LANGUAGE, language).apply(); + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putString(PREF_MULTI_AUDIO_LANGUAGE, language) + .apply(); } public static int getMultiAudioChannelCount(Context context) { - return PreferenceManager.getDefaultSharedPreferences(context).getInt( - PREF_MULTI_AUDIO_CHANNEL_COUNT, 0); + return PreferenceManager.getDefaultSharedPreferences(context) + .getInt(PREF_MULTI_AUDIO_CHANNEL_COUNT, 0); } public static void setMultiAudioChannelCount(Context context, int channelCount) { - PreferenceManager.getDefaultSharedPreferences(context).edit().putInt( - PREF_MULTI_AUDIO_CHANNEL_COUNT, channelCount).apply(); + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putInt(PREF_MULTI_AUDIO_CHANNEL_COUNT, channelCount) + .apply(); } - public static void setDvrPlaybackTrackSettings(Context context, int trackType, - TvTrackInfo info) { + public static void setDvrPlaybackTrackSettings( + Context context, int trackType, TvTrackInfo info) { if (trackType == TvTrackInfo.TYPE_AUDIO) { if (info == null) { - PreferenceManager.getDefaultSharedPreferences(context).edit() - .remove(PREF_DVR_MULTI_AUDIO_ID).apply(); + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .remove(PREF_DVR_MULTI_AUDIO_ID) + .apply(); } else { - PreferenceManager.getDefaultSharedPreferences(context).edit() + PreferenceManager.getDefaultSharedPreferences(context) + .edit() .putString(PREF_DVR_MULTI_AUDIO_LANGUAGE, info.getLanguage()) .putInt(PREF_DVR_MULTI_AUDIO_CHANNEL_COUNT, info.getAudioChannelCount()) - .putString(PREF_DVR_MULTI_AUDIO_ID, info.getId()).apply(); + .putString(PREF_DVR_MULTI_AUDIO_ID, info.getId()) + .apply(); } } else if (trackType == TvTrackInfo.TYPE_SUBTITLE) { if (info == null) { - PreferenceManager.getDefaultSharedPreferences(context).edit() - .remove(PREF_DVR_SUBTITLE_ID).apply(); + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .remove(PREF_DVR_SUBTITLE_ID) + .apply(); } else { - PreferenceManager.getDefaultSharedPreferences(context).edit() + PreferenceManager.getDefaultSharedPreferences(context) + .edit() .putString(PREF_DVR_SUBTITLE_LANGUAGE, info.getLanguage()) - .putString(PREF_DVR_SUBTITLE_ID, info.getId()).apply(); + .putString(PREF_DVR_SUBTITLE_ID, info.getId()) + .apply(); } } } - public static TvTrackInfo getDvrPlaybackTrackSettings(Context context, - int trackType) { + public static TvTrackInfo getDvrPlaybackTrackSettings(Context context, int trackType) { String language; String trackId; int channelCount; @@ -136,7 +153,9 @@ public final class TvSettings { language = pref.getString(PREF_DVR_MULTI_AUDIO_LANGUAGE, null); channelCount = pref.getInt(PREF_DVR_MULTI_AUDIO_CHANNEL_COUNT, 0); return new TvTrackInfo.Builder(trackType, trackId) - .setLanguage(language).setAudioChannelCount(channelCount).build(); + .setLanguage(language) + .setAudioChannelCount(channelCount) + .build(); } else if (trackType == TvTrackInfo.TYPE_SUBTITLE) { trackId = pref.getString(PREF_DVR_SUBTITLE_ID, null); if (trackId == null) { @@ -153,16 +172,20 @@ public final class TvSettings { public static void addContentRatingSystem(Context context, String id) { Set<String> contentRatingSystemSet = getContentRatingSystemSet(context); if (contentRatingSystemSet.add(id)) { - PreferenceManager.getDefaultSharedPreferences(context).edit() - .putStringSet(PREF_CONTENT_RATING_SYSTEMS, contentRatingSystemSet).apply(); + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putStringSet(PREF_CONTENT_RATING_SYSTEMS, contentRatingSystemSet) + .apply(); } } public static void removeContentRatingSystem(Context context, String id) { Set<String> contentRatingSystemSet = getContentRatingSystemSet(context); if (contentRatingSystemSet.remove(id)) { - PreferenceManager.getDefaultSharedPreferences(context).edit() - .putStringSet(PREF_CONTENT_RATING_SYSTEMS, contentRatingSystemSet).apply(); + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putStringSet(PREF_CONTENT_RATING_SYSTEMS, contentRatingSystemSet) + .apply(); } } @@ -176,25 +199,28 @@ public final class TvSettings { */ public static boolean isContentRatingSystemSet(Context context) { return PreferenceManager.getDefaultSharedPreferences(context) - .getStringSet(PREF_CONTENT_RATING_SYSTEMS, null) != null; + .getStringSet(PREF_CONTENT_RATING_SYSTEMS, null) + != null; } private static Set<String> getContentRatingSystemSet(Context context) { - return new HashSet<>(PreferenceManager.getDefaultSharedPreferences(context) - .getStringSet(PREF_CONTENT_RATING_SYSTEMS, Collections.emptySet())); + return new HashSet<>( + PreferenceManager.getDefaultSharedPreferences(context) + .getStringSet(PREF_CONTENT_RATING_SYSTEMS, Collections.emptySet())); } @ContentRatingLevel @SuppressWarnings("ResourceType") public static int getContentRatingLevel(Context context) { - return PreferenceManager.getDefaultSharedPreferences(context).getInt( - PREF_CONTENT_RATING_LEVEL, CONTENT_RATING_LEVEL_NONE); + return PreferenceManager.getDefaultSharedPreferences(context) + .getInt(PREF_CONTENT_RATING_LEVEL, CONTENT_RATING_LEVEL_NONE); } - public static void setContentRatingLevel(Context context, - @ContentRatingLevel int level) { - PreferenceManager.getDefaultSharedPreferences(context).edit().putInt( - PREF_CONTENT_RATING_LEVEL, level).apply(); + public static void setContentRatingLevel(Context context, @ContentRatingLevel int level) { + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putInt(PREF_CONTENT_RATING_LEVEL, level) + .apply(); } /** @@ -202,8 +228,8 @@ public final class TvSettings { * repeatedly). */ public static long getDisablePinUntil(Context context) { - return PreferenceManager.getDefaultSharedPreferences(context).getLong( - PREF_DISABLE_PIN_UNTIL, 0); + return PreferenceManager.getDefaultSharedPreferences(context) + .getLong(PREF_DISABLE_PIN_UNTIL, 0); } /** @@ -211,7 +237,9 @@ public final class TvSettings { * repeatedly). */ public static void setDisablePinUntil(Context context, long timeMillis) { - PreferenceManager.getDefaultSharedPreferences(context).edit().putLong( - PREF_DISABLE_PIN_UNTIL, timeMillis).apply(); + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putLong(PREF_DISABLE_PIN_UNTIL, timeMillis) + .apply(); } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/util/TvTrackInfoUtils.java b/src/com/android/tv/util/TvTrackInfoUtils.java index 667cc9bf..09874502 100644 --- a/src/com/android/tv/util/TvTrackInfoUtils.java +++ b/src/com/android/tv/util/TvTrackInfoUtils.java @@ -16,27 +16,24 @@ package com.android.tv.util; import android.media.tv.TvTrackInfo; - import java.util.Comparator; import java.util.List; -/** - * Static utilities for {@link TvTrackInfo}. - */ +/** Static utilities for {@link TvTrackInfo}. */ public class TvTrackInfoUtils { /** * Compares how closely two {@link android.media.tv.TvTrackInfo}s match {@code language}, {@code * channelCount} and {@code id} in that precedence. * - * @param id The track id to match. - * @param language The language to match. + * @param id The track id to match. + * @param language The language to match. * @param channelCount The channel count to match. * @return -1 if lhs is a worse match, 0 if lhs and rhs match equally and 1 if lhs is a better - * match. + * match. */ - public static Comparator<TvTrackInfo> createComparator(final String id, final String language, - final int channelCount) { + public static Comparator<TvTrackInfo> createComparator( + final String id, final String language, final int channelCount) { return new Comparator<TvTrackInfo>() { @Override @@ -52,15 +49,17 @@ public class TvTrackInfoUtils { } // Assumes {@code null} language matches to any language since it means user hasn't // selected any track before or selected a track without language information. - boolean lhsLangMatch = language == null || Utils.isEqualLanguage(lhs.getLanguage(), - language); - boolean rhsLangMatch = language == null || Utils.isEqualLanguage(rhs.getLanguage(), - language); + boolean lhsLangMatch = + language == null || Utils.isEqualLanguage(lhs.getLanguage(), language); + boolean rhsLangMatch = + language == null || Utils.isEqualLanguage(rhs.getLanguage(), language); if (lhsLangMatch && rhsLangMatch) { - boolean lhsCountMatch = lhs.getType() != TvTrackInfo.TYPE_AUDIO - || lhs.getAudioChannelCount() == channelCount; - boolean rhsCountMatch = rhs.getType() != TvTrackInfo.TYPE_AUDIO - || rhs.getAudioChannelCount() == channelCount; + boolean lhsCountMatch = + lhs.getType() != TvTrackInfo.TYPE_AUDIO + || lhs.getAudioChannelCount() == channelCount; + boolean rhsCountMatch = + rhs.getType() != TvTrackInfo.TYPE_AUDIO + || rhs.getAudioChannelCount() == channelCount; if (lhsCountMatch && rhsCountMatch) { return Boolean.compare(lhs.getId().equals(id), rhs.getId().equals(id)); } else { @@ -74,16 +73,16 @@ public class TvTrackInfoUtils { } /** - * Selects the best TvTrackInfo available or the first if none matches. + * Selects the best TvTrackInfo available or the first if none matches. * - * @param tracks The tracks to choose from - * @param id The track id to match. - * @param language The language to match. + * @param tracks The tracks to choose from + * @param id The track id to match. + * @param language The language to match. * @param channelCount The channel count to match. * @return the best matching track or the first one if none matches. */ - public static TvTrackInfo getBestTrackInfo(List<TvTrackInfo> tracks, String id, String language, - int channelCount) { + public static TvTrackInfo getBestTrackInfo( + List<TvTrackInfo> tracks, String id, String language, int channelCount) { if (tracks == null) { return null; } @@ -97,6 +96,5 @@ public class TvTrackInfoUtils { return best; } - private TvTrackInfoUtils() { - } -}
\ No newline at end of file + private TvTrackInfoUtils() {} +} diff --git a/src/com/android/tv/util/TvUriMatcher.java b/src/com/android/tv/util/TvUriMatcher.java index 3d91cdad..9e74117e 100644 --- a/src/com/android/tv/util/TvUriMatcher.java +++ b/src/com/android/tv/util/TvUriMatcher.java @@ -21,22 +21,25 @@ import android.content.UriMatcher; import android.media.tv.TvContract; import android.net.Uri; import android.support.annotation.IntDef; - import com.android.tv.search.LocalSearchProvider; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -/** - * Utility class to aid in matching URIs in TvProvider. - */ +/** Utility class to aid in matching URIs in TvProvider. */ public class TvUriMatcher { private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); @Retention(RetentionPolicy.SOURCE) - @IntDef({MATCH_CHANNEL, MATCH_CHANNEL_ID, MATCH_PROGRAM, MATCH_PROGRAM_ID, - MATCH_RECORDED_PROGRAM, MATCH_RECORDED_PROGRAM_ID, MATCH_WATCHED_PROGRAM_ID, - MATCH_ON_DEVICE_SEARCH}) + @IntDef({ + MATCH_CHANNEL, + MATCH_CHANNEL_ID, + MATCH_PROGRAM, + MATCH_PROGRAM_ID, + MATCH_RECORDED_PROGRAM, + MATCH_RECORDED_PROGRAM_ID, + MATCH_WATCHED_PROGRAM_ID, + MATCH_ON_DEVICE_SEARCH + }) private @interface TvProviderUriMatchCode {} /** The code for the channels URI. */ public static final int MATCH_CHANNEL = 1; @@ -54,6 +57,7 @@ public class TvUriMatcher { public static final int MATCH_WATCHED_PROGRAM_ID = 7; /** The code for the on-device search URI. */ public static final int MATCH_ON_DEVICE_SEARCH = 8; + static { URI_MATCHER.addURI(TvContract.AUTHORITY, "channel", MATCH_CHANNEL); URI_MATCHER.addURI(TvContract.AUTHORITY, "channel/#", MATCH_CHANNEL_ID); @@ -62,11 +66,13 @@ public class TvUriMatcher { URI_MATCHER.addURI(TvContract.AUTHORITY, "recorded_program", MATCH_RECORDED_PROGRAM); URI_MATCHER.addURI(TvContract.AUTHORITY, "recorded_program/#", MATCH_RECORDED_PROGRAM_ID); URI_MATCHER.addURI(TvContract.AUTHORITY, "watched_program/#", MATCH_WATCHED_PROGRAM_ID); - URI_MATCHER.addURI(LocalSearchProvider.AUTHORITY, - SearchManager.SUGGEST_URI_PATH_QUERY + "/*", MATCH_ON_DEVICE_SEARCH); + URI_MATCHER.addURI( + LocalSearchProvider.AUTHORITY, + SearchManager.SUGGEST_URI_PATH_QUERY + "/*", + MATCH_ON_DEVICE_SEARCH); } - private TvUriMatcher() { } + private TvUriMatcher() {} /** * Try to match against the path in a url. @@ -74,7 +80,8 @@ public class TvUriMatcher { * @see UriMatcher#match */ @SuppressWarnings("WrongConstant") - @TvProviderUriMatchCode public static int match(Uri uri) { + @TvProviderUriMatchCode + public static int match(Uri uri) { return URI_MATCHER.match(uri); } } diff --git a/src/com/android/tv/util/Utils.java b/src/com/android/tv/util/Utils.java index d11bab3c..a75bd446 100644 --- a/src/com/android/tv/util/Utils.java +++ b/src/com/android/tv/util/Utils.java @@ -38,22 +38,16 @@ import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; import android.text.TextUtils; import android.text.format.DateUtils; -import android.util.ArraySet; import android.util.Log; import android.view.View; - -import com.android.tv.ApplicationSingletons; import com.android.tv.R; -import com.android.tv.TvApplication; -import com.android.tv.common.BuildConfig; +import com.android.tv.TvSingletons; import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; +import com.android.tv.common.util.Clock; import com.android.tv.data.GenreItems; import com.android.tv.data.Program; import com.android.tv.data.StreamInfo; -import com.android.tv.experiments.Experiments; - -import java.io.File; +import com.android.tv.data.api.Channel; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; @@ -69,18 +63,13 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -/** - * A class that includes convenience methods for accessing TvProvider database. - */ +/** A class that includes convenience methods for accessing TvProvider database. */ public class Utils { private static final String TAG = "Utils"; private static final boolean DEBUG = false; - private static final SimpleDateFormat ISO_8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", - Locale.US); - public static final String EXTRA_KEY_ACTION = "action"; - public static final String EXTRA_ACTION_SHOW_TV_INPUT ="show_tv_input"; + public static final String EXTRA_ACTION_SHOW_TV_INPUT = "show_tv_input"; public static final String EXTRA_KEY_FROM_LAUNCHER = "from_launcher"; public static final String EXTRA_KEY_RECORDED_PROGRAM_ID = "recorded_program_id"; public static final String EXTRA_KEY_RECORDED_PROGRAM_SEEK_TIME = "recorded_program_seek_time"; @@ -97,8 +86,7 @@ public class Utils { private static final String PREF_KEY_LAST_WATCHED_CHANNEL_URI = "last_watched_channel_uri"; private static final String PREF_KEY_LAST_WATCHED_TUNER_INPUT_ID = "last_watched_tuner_input_id"; - private static final String PREF_KEY_RECORDING_FAILED_REASONS = - "recording_failed_reasons"; + private static final String PREF_KEY_RECORDING_FAILED_REASONS = "recording_failed_reasons"; private static final String PREF_KEY_FAILED_SCHEDULED_RECORDING_INFO_SET = "failed_scheduled_recording_info_set"; @@ -121,15 +109,6 @@ public class Utils { private static final long HALF_MINUTE_MS = TimeUnit.SECONDS.toMillis(30); private static final long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1); - // Hardcoded list for known bundled inputs not written by OEM/SOCs. - // Bundled (system) inputs not in the list will get the high priority - // so they and their channels come first in the UI. - private static final Set<String> BUNDLED_PACKAGE_SET = new ArraySet<>(); - - static { - BUNDLED_PACKAGE_SET.add("com.android.tv"); - } - private enum AspectRatio { ASPECT_RATIO_4_3(4, 3), ASPECT_RATIO_16_9(16, 9), @@ -150,13 +129,11 @@ public class Utils { } } - private Utils() { - } + private Utils() {} public static String buildSelectionForIds(String idName, List<Long> ids) { StringBuilder sb = new StringBuilder(); - sb.append(idName).append(" in (") - .append(ids.get(0)); + sb.append(idName).append(" in (").append(ids.get(0)); for (int i = 1; i < ids.size(); ++i) { sb.append(",").append(ids.get(i)); } @@ -171,8 +148,8 @@ public class Utils { } Uri channelUri = TvContract.buildChannelUri(channelId); String[] projection = {TvContract.Channels.COLUMN_INPUT_ID}; - try (Cursor cursor = context.getContentResolver() - .query(channelUri, projection, null, null, null)) { + try (Cursor cursor = + context.getContentResolver().query(channelUri, projection, null, null, null)) { if (cursor != null && cursor.moveToNext()) { return Utils.intern(cursor.getString(0)); } @@ -185,60 +162,61 @@ public class Utils { Log.e(TAG, "setLastWatchedChannel: channel cannot be null"); return; } - PreferenceManager.getDefaultSharedPreferences(context).edit() - .putString(PREF_KEY_LAST_WATCHED_CHANNEL_URI, channel.getUri().toString()).apply(); + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putString(PREF_KEY_LAST_WATCHED_CHANNEL_URI, channel.getUri().toString()) + .apply(); if (!channel.isPassthrough()) { long channelId = channel.getId(); if (channel.getId() < 0) { throw new IllegalArgumentException("channelId should be equal to or larger than 0"); } - PreferenceManager.getDefaultSharedPreferences(context).edit() + PreferenceManager.getDefaultSharedPreferences(context) + .edit() .putLong(PREF_KEY_LAST_WATCHED_CHANNEL_ID, channelId) - .putLong(PREF_KEY_LAST_WATCHED_CHANNEL_ID_FOR_INPUT + channel.getInputId(), + .putLong( + PREF_KEY_LAST_WATCHED_CHANNEL_ID_FOR_INPUT + channel.getInputId(), channelId) .putString(PREF_KEY_LAST_WATCHED_TUNER_INPUT_ID, channel.getInputId()) .apply(); } } - /** - * Sets recording failed reason. - */ + /** Sets recording failed reason. */ public static void setRecordingFailedReason(Context context, int reason) { long reasons = getRecordingFailedReasons(context) | 0x1 << reason; - PreferenceManager.getDefaultSharedPreferences(context).edit() + PreferenceManager.getDefaultSharedPreferences(context) + .edit() .putLong(PREF_KEY_RECORDING_FAILED_REASONS, reasons) .apply(); } - /** - * Adds the info of failed scheduled recording. - */ - public static void addFailedScheduledRecordingInfo(Context context, - String scheduledRecordingInfo) { + /** Adds the info of failed scheduled recording. */ + public static void addFailedScheduledRecordingInfo( + Context context, String scheduledRecordingInfo) { Set<String> failedScheduledRecordingInfoSet = getFailedScheduledRecordingInfoSet(context); failedScheduledRecordingInfoSet.add(scheduledRecordingInfo); - PreferenceManager.getDefaultSharedPreferences(context).edit() - .putStringSet(PREF_KEY_FAILED_SCHEDULED_RECORDING_INFO_SET, + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putStringSet( + PREF_KEY_FAILED_SCHEDULED_RECORDING_INFO_SET, failedScheduledRecordingInfoSet) .apply(); } - /** - * Clears the failed scheduled recording info set. - */ + /** Clears the failed scheduled recording info set. */ public static void clearFailedScheduledRecordingInfoSet(Context context) { - PreferenceManager.getDefaultSharedPreferences(context).edit() + PreferenceManager.getDefaultSharedPreferences(context) + .edit() .remove(PREF_KEY_FAILED_SCHEDULED_RECORDING_INFO_SET) .apply(); } - /** - * Clears recording failed reason. - */ + /** Clears recording failed reason. */ public static void clearRecordingFailedReason(Context context, int reason) { long reasons = getRecordingFailedReasons(context) & ~(0x1 << reason); - PreferenceManager.getDefaultSharedPreferences(context).edit() + PreferenceManager.getDefaultSharedPreferences(context) + .edit() .putLong(PREF_KEY_RECORDING_FAILED_REASONS, reasons) .apply(); } @@ -258,9 +236,7 @@ public class Utils { .getString(PREF_KEY_LAST_WATCHED_CHANNEL_URI, null); } - /** - * Returns the last watched tuner input id. - */ + /** Returns the last watched tuner input id. */ public static String getLastWatchedTunerInputId(Context context) { return PreferenceManager.getDefaultSharedPreferences(context) .getString(PREF_KEY_LAST_WATCHED_TUNER_INPUT_ID, null); @@ -268,32 +244,28 @@ public class Utils { private static long getRecordingFailedReasons(Context context) { return PreferenceManager.getDefaultSharedPreferences(context) - .getLong(PREF_KEY_RECORDING_FAILED_REASONS, - RECORDING_FAILED_REASON_NONE); + .getLong(PREF_KEY_RECORDING_FAILED_REASONS, RECORDING_FAILED_REASON_NONE); } - /** - * Returns the failed scheduled recordings info set. - */ + /** Returns the failed scheduled recordings info set. */ public static Set<String> getFailedScheduledRecordingInfoSet(Context context) { return PreferenceManager.getDefaultSharedPreferences(context) .getStringSet(PREF_KEY_FAILED_SCHEDULED_RECORDING_INFO_SET, new HashSet<>()); } - /** - * Checks do recording failed reason exist. - */ + /** Checks do recording failed reason exist. */ public static boolean hasRecordingFailedReason(Context context, int reason) { long reasons = getRecordingFailedReasons(context); return (reasons & 0x1 << reason) != 0; } /** - * Returns {@code true}, if {@code uri} specifies an input, which is usually generated - * from {@link TvContract#buildChannelsUriForInput}. + * Returns {@code true}, if {@code uri} specifies an input, which is usually generated from + * {@link TvContract#buildChannelsUriForInput}. */ public static boolean isChannelUriForInput(Uri uri) { - return isTvUri(uri) && PATH_CHANNEL.equals(uri.getPathSegments().get(0)) + return isTvUri(uri) + && PATH_CHANNEL.equals(uri.getPathSegments().get(0)) && !TextUtils.isEmpty(uri.getQueryParameter("input")); } @@ -314,7 +286,8 @@ public class Utils { } private static boolean isTvUri(Uri uri) { - return uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) + return uri != null + && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) && TvContract.AUTHORITY.equals(uri.getAuthority()); } @@ -323,23 +296,17 @@ public class Utils { return pathSegments.size() == 2 && pathSegment.equals(pathSegments.get(0)); } - /** - * Returns {@code true}, if {@code uri} is a programs URI. - */ + /** Returns {@code true}, if {@code uri} is a programs URI. */ public static boolean isProgramsUri(Uri uri) { return isTvUri(uri) && PATH_PROGRAM.equals(uri.getPathSegments().get(0)); } - /** - * Returns {@code true}, if {@code uri} is a programs URI. - */ + /** Returns {@code true}, if {@code uri} is a programs URI. */ public static boolean isRecordedProgramsUri(Uri uri) { return isTvUri(uri) && PATH_RECORDED_PROGRAM.equals(uri.getPathSegments().get(0)); } - /** - * Gets the info of the program on particular time. - */ + /** Gets the info of the program on particular time. */ @WorkerThread public static Program getProgramAt(Context context, long channelId, long timeMs) { if (channelId == Channel.INVALID_ID) { @@ -355,10 +322,11 @@ public class Utils { Log.w(TAG, message); } } - Uri uri = TvContract.buildProgramsUriForChannel(TvContract.buildChannelUri(channelId), - timeMs, timeMs); - try (Cursor cursor = context.getContentResolver().query(uri, Program.PROJECTION, - null, null, null)) { + Uri uri = + TvContract.buildProgramsUriForChannel( + TvContract.buildChannelUri(channelId), timeMs, timeMs); + try (Cursor cursor = + context.getContentResolver().query(uri, Program.PROJECTION, null, null, null)) { if (cursor != null && cursor.moveToNext()) { return Program.fromCursor(cursor); } @@ -366,55 +334,98 @@ public class Utils { return null; } - /** - * Gets the info of the current program. - */ + /** Gets the info of the current program. */ @WorkerThread public static Program getCurrentProgram(Context context, long channelId) { return getProgramAt(context, channelId, System.currentTimeMillis()); } - /** - * Returns the round off minutes when convert milliseconds to minutes. - */ + /** Returns the round off minutes when convert milliseconds to minutes. */ public static int getRoundOffMinsFromMs(long millis) { // Round off the result by adding half minute to the original ms. return (int) TimeUnit.MILLISECONDS.toMinutes(millis + HALF_MINUTE_MS); } /** - * Returns duration string according to the date & time format. - * If {@code startUtcMillis} and {@code endUtcMills} are equal, - * formatted time will be returned instead. + * Returns duration string according to the date & time format. If {@code startUtcMillis} and + * {@code endUtcMills} are equal, formatted time will be returned instead. * * @param startUtcMillis start of duration in millis. Should be less than {code endUtcMillis}. * @param endUtcMillis end of duration in millis. Should be larger than {@code startUtcMillis}. - * @param useShortFormat {@code true} if abbreviation is needed to save space. - * In that case, date will be omitted if duration starts from today - * and is less than a day. If it's necessary, - * {@link DateUtils#FORMAT_NUMERIC_DATE} is used otherwise. + * @param useShortFormat {@code true} if abbreviation is needed to save space. In that case, + * date will be omitted if duration starts from today and is less than a day. If it's + * necessary, {@link DateUtils#FORMAT_NUMERIC_DATE} is used otherwise. */ public static String getDurationString( Context context, long startUtcMillis, long endUtcMillis, boolean useShortFormat) { - return getDurationString(context, System.currentTimeMillis(), startUtcMillis, endUtcMillis, - useShortFormat, 0); + return getDurationString( + context, + System.currentTimeMillis(), + startUtcMillis, + endUtcMillis, + useShortFormat, + 0); } - @VisibleForTesting - static String getDurationString(Context context, long baseMillis, long startUtcMillis, - long endUtcMillis, boolean useShortFormat, int flag) { - return getDurationString(context, startUtcMillis, endUtcMillis, - useShortFormat, !isInGivenDay(baseMillis, startUtcMillis), true, flag); + /** + * Returns duration string according to the date & time format. If {@code startUtcMillis} and + * {@code endUtcMills} are equal, formatted time will be returned instead. + * + * @param clock the clock used to get the current time. + * @param startUtcMillis start of duration in millis. Should be less than {code endUtcMillis}. + * @param endUtcMillis end of duration in millis. Should be larger than {@code startUtcMillis}. + * @param useShortFormat {@code true} if abbreviation is needed to save space. In that case, + * date will be omitted if duration starts from today and is less than a day. If it's + * necessary, {@link DateUtils#FORMAT_NUMERIC_DATE} is used otherwise. + */ + public static String getDurationString( + Context context, + Clock clock, + long startUtcMillis, + long endUtcMillis, + boolean useShortFormat) { + return getDurationString( + context, + clock.currentTimeMillis(), + startUtcMillis, + endUtcMillis, + useShortFormat, + 0); } - /** - * Returns duration string according to the time format, may not contain date information. - * Note: At least one of showDate and showTime should be true. + @VisibleForTesting + static String getDurationString( + Context context, + long baseMillis, + long startUtcMillis, + long endUtcMillis, + boolean useShortFormat, + int flag) { + return getDurationString( + context, + startUtcMillis, + endUtcMillis, + useShortFormat, + !isInGivenDay(baseMillis, startUtcMillis), + true, + flag); + } + + /** + * Returns duration string according to the time format, may not contain date information. Note: + * At least one of showDate and showTime should be true. */ - public static String getDurationString(Context context, long startUtcMillis, long endUtcMillis, - boolean useShortFormat, boolean showDate, boolean showTime, int flag) { - flag |= DateUtils.FORMAT_ABBREV_MONTH - | ((useShortFormat) ? DateUtils.FORMAT_NUMERIC_DATE : 0); + public static String getDurationString( + Context context, + long startUtcMillis, + long endUtcMillis, + boolean useShortFormat, + boolean showDate, + boolean showTime, + int flag) { + flag |= + DateUtils.FORMAT_ABBREV_MONTH + | ((useShortFormat) ? DateUtils.FORMAT_NUMERIC_DATE : 0); SoftPreconditions.checkArgument(showTime || showDate); if (showTime) { flag |= DateUtils.FORMAT_SHOW_TIME; @@ -431,20 +442,21 @@ public class Utils { // Do not show date for short format. // Subtracting one day is needed because {@link DateUtils@formatDateRange} // automatically shows date if the duration covers multiple days. - return DateUtils.formatDateRange(context, - startUtcMillis, endUtcMillis - TimeUnit.DAYS.toMillis(1), flag); + return DateUtils.formatDateRange( + context, startUtcMillis, endUtcMillis - TimeUnit.DAYS.toMillis(1), flag); } } // Workaround of b/28740989. // Add 1 msec to endUtcMillis to avoid DateUtils' bug with a duration of 12:00AM~12:00AM. String dateRange = DateUtils.formatDateRange(context, startUtcMillis, endUtcMillis, flag); - return startUtcMillis == endUtcMillis || dateRange.contains("–") ? dateRange + return startUtcMillis == endUtcMillis || dateRange.contains("–") + ? dateRange : DateUtils.formatDateRange(context, startUtcMillis, endUtcMillis + 1, flag); } /** - * Checks if two given time (in milliseconds) are in the same day with regard to the - * locale timezone. + * Checks if two given time (in milliseconds) are in the same day with regard to the locale + * timezone. */ public static boolean isInGivenDay(long dayToMatchInMillis, long subjectTimeInMillis) { TimeZone timeZone = Calendar.getInstance().getTimeZone(); @@ -456,9 +468,7 @@ public class Utils { == Utils.floorTime(subjectTimeInMillis + offset, ONE_DAY_MS); } - /** - * Calculate how many days between two milliseconds. - */ + /** Calculate how many days between two milliseconds. */ public static int computeDateDifference(long startTimeMs, long endTimeMs) { Calendar calFrom = Calendar.getInstance(); Calendar calTo = Calendar.getInstance(); @@ -476,17 +486,23 @@ public class Utils { cal.set(Calendar.MILLISECOND, 0); } - /** - * Returns the last millisecond of a day which the millis belongs to. - */ + /** Returns the last millisecond of a day which the millis belongs to. */ public static long getLastMillisecondOfDay(long millis) { - Calendar calender = Calendar.getInstance(); - calender.setTime(new Date(millis)); - calender.set(Calendar.HOUR_OF_DAY, 23); - calender.set(Calendar.MINUTE, 59); - calender.set(Calendar.SECOND, 59); - calender.set(Calendar.MILLISECOND, 999); - return calender.getTimeInMillis(); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date(millis)); + calendar.set(Calendar.HOUR_OF_DAY, 23); + calendar.set(Calendar.MINUTE, 59); + calendar.set(Calendar.SECOND, 59); + calendar.set(Calendar.MILLISECOND, 999); + return calendar.getTimeInMillis(); + } + + /** Returns the last millisecond of a day which the millis belongs to. */ + public static long getFirstMillisecondOfDay(long millis) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date(millis)); + resetCalendar(calendar); + return calendar.getTimeInMillis(); } public static String getAspectRatioString(int width, int height) { @@ -494,7 +510,7 @@ public class Utils { return ""; } - for (AspectRatio ratio: AspectRatio.values()) { + for (AspectRatio ratio : AspectRatio.values()) { if (Math.abs((float) ratio.height / ratio.width - (float) height / width) < 0.05f) { return ratio.toString(); } @@ -531,11 +547,9 @@ public class Utils { public static String getVideoDefinitionLevelString(Context context, int videoFormat) { switch (videoFormat) { case StreamInfo.VIDEO_DEFINITION_LEVEL_ULTRA_HD: - return context.getResources().getString( - R.string.video_definition_level_ultra_hd); + return context.getResources().getString(R.string.video_definition_level_ultra_hd); case StreamInfo.VIDEO_DEFINITION_LEVEL_FULL_HD: - return context.getResources().getString( - R.string.video_definition_level_full_hd); + return context.getResources().getString(R.string.video_definition_level_full_hd); case StreamInfo.VIDEO_DEFINITION_LEVEL_HD: return context.getResources().getString(R.string.video_definition_level_hd); case StreamInfo.VIDEO_DEFINITION_LEVEL_SD: @@ -570,8 +584,8 @@ public class Utils { return false; } - public static String getMultiAudioString(Context context, TvTrackInfo track, - boolean showSampleRate) { + public static String getMultiAudioString( + Context context, TvTrackInfo track, boolean showSampleRate) { if (track.getType() != TvTrackInfo.TYPE_AUDIO) { throw new IllegalArgumentException("Not an audio track: " + track); } @@ -600,11 +614,17 @@ public class Utils { break; default: if (track.getAudioChannelCount() > 0) { - metadata.append(context.getString(R.string.multi_audio_channel_suffix, - track.getAudioChannelCount())); + metadata.append( + context.getString( + R.string.multi_audio_channel_suffix, + track.getAudioChannelCount())); } else { - Log.d(TAG, "Invalid audio channel count (" + track.getAudioChannelCount() - + ") found for the audio track: " + track); + Log.d( + TAG, + "Invalid audio channel count (" + + track.getAudioChannelCount() + + ") found for the audio track: " + + track); } break; } @@ -628,8 +648,8 @@ public class Utils { if (metadata.length() == 0) { return language; } - return context.getString(R.string.multi_audio_display_string_with_channel, language, - metadata.toString()); + return context.getString( + R.string.multi_audio_display_string_with_channel, language, metadata.toString()); } public static boolean isEqualLanguage(String lang1, String lang2) { @@ -647,19 +667,19 @@ public class Utils { } public static boolean isIntentAvailable(Context context, Intent intent) { - return context.getPackageManager().queryIntentActivities( - intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; + return context.getPackageManager() + .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY) + .size() + > 0; } - /** - * Returns the label for a given input. Returns the custom label, if any. - */ + /** Returns the label for a given input. Returns the custom label, if any. */ public static String loadLabel(Context context, TvInputInfo input) { if (input == null) { return null; } TvInputManagerHelper inputManager = - TvApplication.getSingletons(context).getTvInputManagerHelper(); + TvSingletons.getSingletons(context).getTvInputManagerHelper(); CharSequence customLabel = inputManager.loadCustomLabel(input); String label = (customLabel == null) ? null : customLabel.toString(); if (TextUtils.isEmpty(label)) { @@ -668,9 +688,7 @@ public class Utils { return label; } - /** - * Enable all channels synchronously. - */ + /** Enable all channels synchronously. */ @WorkerThread public static void enableAllChannels(Context context) { ContentValues values = new ContentValues(); @@ -681,46 +699,48 @@ public class Utils { /** * Converts time in milliseconds to a String. * - * @param fullFormat {@code true} for returning date string with a full format - * (e.g., Mon Aug 15 20:08:35 GMT 2016). {@code false} for a short format, - * {e.g., [8/15/16] 8:08 AM}, in which date information would only appears - * when the target time is not today. + * @param fullFormat {@code true} for returning date string with a full format (e.g., Mon Aug 15 + * 20:08:35 GMT 2016). {@code false} for a short format, {e.g., 8/15/16 or 8:08 AM}, in + * which only the time is shown if the time is on the same day as now, and only the date is + * shown if it's a different day. */ public static String toTimeString(long timeMillis, boolean fullFormat) { if (fullFormat) { return new Date(timeMillis).toString(); } else { long currentTime = System.currentTimeMillis(); - return (String) DateUtils.formatSameDayTime(timeMillis, System.currentTimeMillis(), - SimpleDateFormat.SHORT, SimpleDateFormat.SHORT); + return (String) + DateUtils.formatSameDayTime( + timeMillis, + System.currentTimeMillis(), + SimpleDateFormat.SHORT, + SimpleDateFormat.SHORT); } } - /** - * Converts time in milliseconds to a String. - */ + /** Converts time in milliseconds to a String. */ public static String toTimeString(long timeMillis) { return toTimeString(timeMillis, true); } /** - * Converts time in milliseconds to a ISO 8061 string. - */ - public static String toIsoDateTimeString(long timeMillis) { - return ISO_8601.format(new Date(timeMillis)); - } - - /** * Returns a {@link String} object which contains the layout information of the {@code view}. */ public static String toRectString(View view) { return "{" - + "l=" + view.getLeft() - + ",r=" + view.getRight() - + ",t=" + view.getTop() - + ",b=" + view.getBottom() - + ",w=" + view.getWidth() - + ",h=" + view.getHeight() + "}"; + + "l=" + + view.getLeft() + + ",r=" + + view.getRight() + + ",t=" + + view.getTop() + + ",b=" + + view.getBottom() + + ",w=" + + view.getWidth() + + ",h=" + + view.getHeight() + + "}"; } /** @@ -732,16 +752,14 @@ public class Utils { } /** - * Ceils time to the given {@code timeUnit}. For example, if time is 5:32:11 and timeUnit is - * one hour (60 * 60 * 1000), then the output will be 6:00:00. + * Ceils time to the given {@code timeUnit}. For example, if time is 5:32:11 and timeUnit is one + * hour (60 * 60 * 1000), then the output will be 6:00:00. */ public static long ceilTime(long timeMs, long timeUnit) { return timeMs + timeUnit - (timeMs % timeUnit); } - /** - * Returns an {@link String#intern() interned} string or null if the input is null. - */ + /** Returns an {@link String#intern() interned} string or null if the input is null. */ @Nullable public static String intern(@Nullable String string) { return string == null ? null : string.intern(); @@ -749,6 +767,7 @@ public class Utils { /** * Check if the index is valid for the collection, + * * @param collection the collection * @param index the index position to test * @return index >= 0 && index < collection.size(). @@ -757,9 +776,7 @@ public class Utils { return collection != null && (index >= 0 && index < collection.size()); } - /** - * Returns a localized version of the text resource specified by resourceId. - */ + /** Returns a localized version of the text resource specified by resourceId. */ public static CharSequence getTextForLocale(Context context, Locale locale, int resourceId) { if (locale.equals(context.getResources().getConfiguration().locale)) { return context.getText(resourceId); @@ -769,12 +786,12 @@ public class Utils { return context.createConfigurationContext(config).getText(resourceId); } - /** - * Checks where there is any internal TV input. - */ + /** Checks where there is any internal TV input. */ public static boolean hasInternalTvInputs(Context context, boolean tunerInputOnly) { - for (TvInputInfo input : TvApplication.getSingletons(context).getTvInputManagerHelper() - .getTvInputInfos(true, tunerInputOnly)) { + for (TvInputInfo input : + TvSingletons.getSingletons(context) + .getTvInputManagerHelper() + .getTvInputInfos(true, tunerInputOnly)) { if (isInternalTvInput(context, input.getId())) { return true; } @@ -782,13 +799,13 @@ public class Utils { return false; } - /** - * Returns the internal TV inputs. - */ + /** Returns the internal TV inputs. */ public static List<TvInputInfo> getInternalTvInputs(Context context, boolean tunerInputOnly) { List<TvInputInfo> inputs = new ArrayList<>(); - for (TvInputInfo input : TvApplication.getSingletons(context).getTvInputManagerHelper() - .getTvInputInfos(true, tunerInputOnly)) { + for (TvInputInfo input : + TvSingletons.getSingletons(context) + .getTvInputManagerHelper() + .getTvInputInfos(true, tunerInputOnly)) { if (isInternalTvInput(context, input.getId())) { inputs.add(input); } @@ -796,81 +813,41 @@ public class Utils { return inputs; } - /** - * Checks whether the input is internal or not. - */ + /** Checks whether the input is internal or not. */ public static boolean isInternalTvInput(Context context, String inputId) { - return context.getPackageName().equals(ComponentName.unflattenFromString(inputId) - .getPackageName()); + return context.getPackageName() + .equals(ComponentName.unflattenFromString(inputId).getPackageName()); } - /** - * Returns the TV input for the given {@code program}. - */ + /** Returns the TV input for the given {@code program}. */ @Nullable public static TvInputInfo getTvInputInfoForProgram(Context context, Program program) { - if (!Program.isValid(program)) { + if (!Program.isProgramValid(program)) { return null; } return getTvInputInfoForChannelId(context, program.getChannelId()); } - /** - * Returns the TV input for the given channel ID. - */ + /** Returns the TV input for the given channel ID. */ @Nullable public static TvInputInfo getTvInputInfoForChannelId(Context context, long channelId) { - ApplicationSingletons appSingletons = TvApplication.getSingletons(context); - Channel channel = appSingletons.getChannelDataManager().getChannel(channelId); + TvSingletons tvSingletons = TvSingletons.getSingletons(context); + Channel channel = tvSingletons.getChannelDataManager().getChannel(channelId); if (channel == null) { return null; } - return appSingletons.getTvInputManagerHelper().getTvInputInfo(channel.getInputId()); + return tvSingletons.getTvInputManagerHelper().getTvInputInfo(channel.getInputId()); } - /** - * Returns the {@link TvInputInfo} for the given input ID. - */ + /** Returns the {@link TvInputInfo} for the given input ID. */ @Nullable public static TvInputInfo getTvInputInfoForInputId(Context context, String inputId) { - return TvApplication.getSingletons(context).getTvInputManagerHelper() + return TvSingletons.getSingletons(context) + .getTvInputManagerHelper() .getTvInputInfo(inputId); } - /** - * Deletes a file or a directory. - */ - public static void deleteDirOrFile(File fileOrDirectory) { - if (fileOrDirectory.isDirectory()) { - for (File child : fileOrDirectory.listFiles()) { - deleteDirOrFile(child); - } - } - fileOrDirectory.delete(); - } - - /** - * Checks whether a given package is in our bundled package set. - */ - public static boolean isInBundledPackageSet(String packageName) { - return BUNDLED_PACKAGE_SET.contains(packageName); - } - - /** - * Checks whether a given input is a bundled input. - */ - public static boolean isBundledInput(String inputId) { - for (String prefix : BUNDLED_PACKAGE_SET) { - if (inputId.startsWith(prefix + "/")) { - return true; - } - } - return false; - } - - /** - * Returns the canonical genre ID's from the {@code genres}. - */ + /** Returns the canonical genre ID's from the {@code genres}. */ public static int[] getCanonicalGenreIds(String genres) { if (TextUtils.isEmpty(genres)) { return null; @@ -878,9 +855,7 @@ public class Utils { return getCanonicalGenreIds(Genres.decode(genres)); } - /** - * Returns the canonical genre ID's from the {@code genres}. - */ + /** Returns the canonical genre ID's from the {@code genres}. */ public static int[] getCanonicalGenreIds(String[] canonicalGenres) { if (canonicalGenres != null && canonicalGenres.length > 0) { int[] results = new int[canonicalGenres.length]; @@ -901,9 +876,7 @@ public class Utils { return null; } - /** - * Returns the canonical genres for database. - */ + /** Returns the canonical genres for database. */ public static String getCanonicalGenre(int[] canonicalGenreIds) { if (canonicalGenreIds == null || canonicalGenreIds.length == 0) { return null; @@ -916,15 +889,8 @@ public class Utils { } /** - * Returns true if the current user is a developer. - */ - public static boolean isDeveloper() { - return BuildConfig.ENG || Experiments.ENABLE_DEVELOPER_FEATURES.get(); - } - - /** - * Runs the method in main thread. If the current thread is not main thread, block it util - * the method is finished. + * Runs the method in main thread. If the current thread is not main thread, block it util the + * method is finished. */ public static void runInMainThreadAndWait(Runnable runnable) { if (Looper.myLooper() == Looper.getMainLooper()) { diff --git a/src/com/android/tv/util/ViewCache.java b/src/com/android/tv/util/ViewCache.java index ed9a8ff6..b8bdb6b8 100644 --- a/src/com/android/tv/util/ViewCache.java +++ b/src/com/android/tv/util/ViewCache.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * 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.android.tv.util; import android.content.Context; @@ -5,22 +20,17 @@ import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import java.util.ArrayList; -/** - * A cache for the views. - */ +/** A cache for the views. */ public class ViewCache { - private final static SparseArray<ArrayList<View>> mViews = new SparseArray(); + private static final SparseArray<ArrayList<View>> mViews = new SparseArray(); private static ViewCache sViewCache; - private ViewCache() { } + private ViewCache() {} - /** - * Returns an instance of the view cache. - */ + /** Returns an instance of the view cache. */ public static ViewCache getInstance() { if (sViewCache == null) { sViewCache = new ViewCache(); @@ -28,16 +38,12 @@ public class ViewCache { return sViewCache; } - /** - * Returns if the view cache is empty. - */ + /** Returns if the view cache is empty. */ public boolean isEmpty() { return mViews.size() == 0; } - /** - * Stores a view into this view cache. - */ + /** Stores a view into this view cache. */ public void putView(int resId, View view) { ArrayList<View> views = mViews.get(resId); if (views == null) { @@ -47,9 +53,7 @@ public class ViewCache { views.add(view); } - /** - * Stores multi specific views into the view cache. - */ + /** Stores multi specific views into the view cache. */ public void putView(Context context, int resId, ViewGroup fakeParent, int num) { LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -64,9 +68,7 @@ public class ViewCache { } } - /** - * Returns the view for specific resource id. - */ + /** Returns the view for specific resource id. */ public View getView(int resId) { ArrayList<View> views = mViews.get(resId); if (views != null && !views.isEmpty()) { @@ -80,9 +82,7 @@ public class ViewCache { } } - /** - * Returns the view if exists, or create a new view for the specific resource id. - */ + /** Returns the view if exists, or create a new view for the specific resource id. */ public View getOrCreateView(LayoutInflater inflater, int resId, ViewGroup container) { View view = getView(resId); if (view == null) { @@ -91,9 +91,7 @@ public class ViewCache { return view; } - /** - * Clears the view cache. - */ + /** Clears the view cache. */ public void clear() { mViews.clear(); } diff --git a/src/com/android/tv/util/account/AccountHelper.java b/src/com/android/tv/util/account/AccountHelper.java new file mode 100644 index 00000000..e98b42ec --- /dev/null +++ b/src/com/android/tv/util/account/AccountHelper.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * 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.android.tv.util.account; + +import android.accounts.Account; +import android.support.annotation.Nullable; + +/** Helper methods for getting and selecting a user account. */ +public interface AccountHelper { + /** Returns the currently selected account or {@code null} if none is selected. */ + @Nullable + Account getSelectedAccount(); + /** + * Selects the first account available. + * + * @return selected account or {@code null} if none is selected. + */ + @Nullable + Account selectFirstAccount(); + + /** Returns all eligible accounts . */ + @Nullable + Account getFirstEligibleAccount(); +} diff --git a/src/com/android/tv/util/AccountHelper.java b/src/com/android/tv/util/account/AccountHelperImpl.java index ece13de1..58fbd27e 100644 --- a/src/com/android/tv/util/AccountHelper.java +++ b/src/com/android/tv/util/account/AccountHelperImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,43 +14,32 @@ * limitations under the License. */ -package com.android.tv.util; +package com.android.tv.util.account; import android.accounts.Account; import android.content.Context; import android.content.SharedPreferences; -import android.os.RemoteException; import android.preference.PreferenceManager; import android.support.annotation.Nullable; -import android.util.Log; - -import java.util.Arrays; - -/** - * Helper methods for getting and selecting a user account. - */ -public class AccountHelper { - private static final String TAG = "AccountHelper"; - private static final boolean DEBUG = false; +/** Helper methods for getting and selecting a user account. */ +public class AccountHelperImpl implements com.android.tv.util.account.AccountHelper { private static final String SELECTED_ACCOUNT = "android.tv.livechannels.selected_account"; - private final Context mContext; + protected final Context mContext; private final SharedPreferences mDefaultPreferences; - @Nullable - private Account mSelectedAccount; + @Nullable private Account mSelectedAccount; - public AccountHelper(Context context) { + public AccountHelperImpl(Context context) { mContext = context.getApplicationContext(); mDefaultPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); } - /** - * Returns the currently selected account or {@code null} if none is selected. - */ + /** Returns the currently selected account or {@code null} if none is selected. */ + @Override @Nullable - public Account getSelectedAccount() { + public final Account getSelectedAccount() { String accountId = mDefaultPreferences.getString(SELECTED_ACCOUNT, null); if (accountId == null) { return null; @@ -68,9 +57,11 @@ public class AccountHelper { } /** - * Returns all eligible accounts . + * Returns all eligible accounts. + * + * <p>Override this method to return the accounts needed. */ - private Account[] getEligibleAccounts() { + protected Account[] getEligibleAccounts() { return new Account[0]; } @@ -79,8 +70,9 @@ public class AccountHelper { * * @return selected account or {@code null} if none is selected. */ + @Override @Nullable - public Account selectFirstAccount() { + public final Account selectFirstAccount() { Account account = getFirstEligibleAccount(); if (account != null) { selectAccount(account); @@ -93,19 +85,17 @@ public class AccountHelper { * * @return first account or {@code null} if none is eligible. */ + @Override @Nullable - public Account getFirstEligibleAccount() { + public final Account getFirstEligibleAccount() { Account[] accounts = getEligibleAccounts(); return accounts.length == 0 ? null : accounts[0]; } - /** - * Sets the given account as the selected account. - */ + /** Sets the given account as the selected account. */ private void selectAccount(Account account) { - SharedPreferences defaultPreferences = PreferenceManager - .getDefaultSharedPreferences(mContext); + SharedPreferences defaultPreferences = + PreferenceManager.getDefaultSharedPreferences(mContext); defaultPreferences.edit().putString(SELECTED_ACCOUNT, account.name).commit(); } } - diff --git a/src/com/android/tv/util/BitmapUtils.java b/src/com/android/tv/util/images/BitmapUtils.java index fbaab023..d6bd5a31 100644 --- a/src/com/android/tv/util/BitmapUtils.java +++ b/src/com/android/tv/util/images/BitmapUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.tv.util; +package com.android.tv.util.images; import android.content.ContentResolver; import android.content.Context; @@ -29,7 +29,7 @@ import android.net.Uri; import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Log; - +import com.android.tv.common.util.NetworkTrafficTags; import java.io.BufferedInputStream; import java.io.Closeable; import java.io.IOException; @@ -45,12 +45,14 @@ public final class BitmapUtils { // The value of 64K, for MARK_READ_LIMIT, is chosen to be eight times the default buffer size // of BufferedInputStream (8K) allowing it to double its buffers three times. Also it is a // fairly reasonable value, not using too much memory and being large enough for most cases. - private static final int MARK_READ_LIMIT = 64 * 1024; // 64K + private static final int MARK_READ_LIMIT = 64 * 1024; // 64K - private static final int CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION = 3000; // 3 sec - private static final int READ_TIMEOUT_MS_FOR_URLCONNECTION = 10000; // 10 sec + private static final int CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION = 3000; // 3 sec + private static final int READ_TIMEOUT_MS_FOR_URLCONNECTION = 10000; // 10 sec - private BitmapUtils() { /* cannot be instantiated */ } + private BitmapUtils() { + /* cannot be instantiated */ + } public static Bitmap scaleBitmap(Bitmap bm, int maxWidth, int maxHeight) { Rect rect = calculateNewSize(bm, maxWidth, maxHeight); @@ -59,7 +61,8 @@ public final class BitmapUtils { public static Bitmap getScaledMutableBitmap(Bitmap bm, int maxWidth, int maxHeight) { Bitmap scaledBitmap = scaleBitmap(bm, maxWidth, maxHeight); - return scaledBitmap.isMutable() ? scaledBitmap + return scaledBitmap.isMutable() + ? scaledBitmap : scaledBitmap.copy(Bitmap.Config.ARGB_8888, true); } @@ -77,17 +80,17 @@ public final class BitmapUtils { return rect; } - public static ScaledBitmapInfo createScaledBitmapInfo(String id, Bitmap bm, int maxWidth, - int maxHeight) { - return new ScaledBitmapInfo(id, scaleBitmap(bm, maxWidth, maxHeight), + public static ScaledBitmapInfo createScaledBitmapInfo( + String id, Bitmap bm, int maxWidth, int maxHeight) { + return new ScaledBitmapInfo( + id, + scaleBitmap(bm, maxWidth, maxHeight), calculateInSampleSize(bm.getWidth(), bm.getHeight(), maxWidth, maxHeight)); } - /** - * Decode large sized bitmap into requested size. - */ - public static ScaledBitmapInfo decodeSampledBitmapFromUriString(Context context, - String uriString, int reqWidth, int reqHeight) { + /** Decode large sized bitmap into requested size. */ + public static ScaledBitmapInfo decodeSampledBitmapFromUriString( + Context context, String uriString, int reqWidth, int reqHeight) { if (TextUtils.isEmpty(uriString)) { return null; } @@ -162,8 +165,8 @@ public final class BitmapUtils { return urlConnection; } - private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, - int reqHeight) { + private static int calculateInSampleSize( + BitmapFactory.Options options, int reqWidth, int reqHeight) { return calculateInSampleSize(options.outWidth, options.outHeight, reqWidth, reqHeight); } @@ -187,7 +190,7 @@ public final class BitmapUtils { closeable.close(); } catch (IOException e) { // Log and continue. - Log.w(TAG,"Error closing " + closeable, e); + Log.w(TAG, "Error closing " + closeable, e); } } if (urlConnection instanceof HttpURLConnection) { @@ -195,21 +198,13 @@ public final class BitmapUtils { } } - /** - * A wrapper class which contains the loaded bitmap and the scaling information. - */ + /** A wrapper class which contains the loaded bitmap and the scaling information. */ public static class ScaledBitmapInfo { - /** - * The id of bitmap, usually this is the URI of the original. - */ - @NonNull - public final String id; + /** The id of bitmap, usually this is the URI of the original. */ + @NonNull public final String id; - /** - * The loaded bitmap object. - */ - @NonNull - public final Bitmap bitmap; + /** The loaded bitmap object. */ + @NonNull public final Bitmap bitmap; /** * The scaling factor to the original bitmap. It should be an positive integer. @@ -222,8 +217,8 @@ public final class BitmapUtils { * A constructor. * * @param bitmap The loaded bitmap object. - * @param inSampleSize The sampling size. - * See {@link android.graphics.BitmapFactory.Options#inSampleSize} + * @param inSampleSize The sampling size. See {@link + * android.graphics.BitmapFactory.Options#inSampleSize} */ public ScaledBitmapInfo(@NonNull String id, @NonNull Bitmap bitmap, int inSampleSize) { this.id = id; @@ -232,10 +227,9 @@ public final class BitmapUtils { } /** - * Checks if the bitmap needs to be reloaded. The scaling is performed by power 2. - * The bitmap can be reloaded only if the required width or height is greater then or equal - * to the existing bitmap. - * If the full sized bitmap is already loaded, returns {@code false}. + * Checks if the bitmap needs to be reloaded. The scaling is performed by power 2. The + * bitmap can be reloaded only if the required width or height is greater then or equal to + * the existing bitmap. If the full sized bitmap is already loaded, returns {@code false}. * * @see android.graphics.BitmapFactory.Options#inSampleSize */ @@ -245,26 +239,41 @@ public final class BitmapUtils { return false; } Rect size = calculateNewSize(this.bitmap, reqWidth, reqHeight); - boolean reload = (size.right >= bitmap.getWidth() * 2 - || size.bottom >= bitmap.getHeight() * 2); + boolean reload = + (size.right >= bitmap.getWidth() * 2 || size.bottom >= bitmap.getHeight() * 2); if (DEBUG) { - Log.d(TAG, "needToReload(" + reqWidth + ", " + reqHeight + ")=" + reload - + " because the new size would be " + size + " for " + this); + Log.d( + TAG, + "needToReload(" + + reqWidth + + ", " + + reqHeight + + ")=" + + reload + + " because the new size would be " + + size + + " for " + + this); } return reload; } - /** - * Returns {@code true} if a request the size of {@code other} would need a reload. - */ - public boolean needToReload(ScaledBitmapInfo other){ + /** Returns {@code true} if a request the size of {@code other} would need a reload. */ + public boolean needToReload(ScaledBitmapInfo other) { return needToReload(other.bitmap.getWidth(), other.bitmap.getHeight()); } @Override public String toString() { - return "ScaledBitmapInfo[" + id + "](in=" + inSampleSize + ", w=" + bitmap.getWidth() - + ", h=" + bitmap.getHeight() + ")"; + return "ScaledBitmapInfo[" + + id + + "](in=" + + inSampleSize + + ", w=" + + bitmap.getWidth() + + ", h=" + + bitmap.getHeight() + + ")"; } } diff --git a/src/com/android/tv/util/ImageCache.java b/src/com/android/tv/util/images/ImageCache.java index b413c364..e260d67a 100644 --- a/src/com/android/tv/util/ImageCache.java +++ b/src/com/android/tv/util/images/ImageCache.java @@ -14,18 +14,15 @@ * limitations under the License. */ -package com.android.tv.util; +package com.android.tv.util.images; import android.support.annotation.VisibleForTesting; import android.util.Log; import android.util.LruCache; +import com.android.tv.common.memory.MemoryManageable; +import com.android.tv.util.images.BitmapUtils.ScaledBitmapInfo; -import com.android.tv.common.MemoryManageable; -import com.android.tv.util.BitmapUtils.ScaledBitmapInfo; - -/** - * A convenience class for caching bitmap. - */ +/** A convenience class for caching bitmap. */ public class ImageCache implements MemoryManageable { private static final float MAX_CACHE_SIZE_PERCENT = 0.8f; private static final float MIN_CACHE_SIZE_PERCENT = 0.05f; @@ -48,16 +45,17 @@ public class ImageCache implements MemoryManageable { if (DEBUG) { Log.d(TAG, "Memory cache created (size = " + memCacheSize + " Kbytes)"); } - mMemoryCache = new LruCache<String, ScaledBitmapInfo>(memCacheSize) { - /** - * Measure item size in kilobytes rather than units which is more practical for a bitmap - * cache - */ - @Override - protected int sizeOf(String key, ScaledBitmapInfo bitmapInfo) { - return (bitmapInfo.bitmap.getByteCount() + 1023) / 1024; - } - }; + mMemoryCache = + new LruCache<String, ScaledBitmapInfo>(memCacheSize) { + /** + * Measure item size in kilobytes rather than units which is more practical for + * a bitmap cache + */ + @Override + protected int sizeOf(String key, ScaledBitmapInfo bitmapInfo) { + return (bitmapInfo.bitmap.getByteCount() + 1023) / 1024; + } + }; } private static ImageCache sImageCache; @@ -67,7 +65,7 @@ public class ImageCache implements MemoryManageable { * param. * * @param memCacheSizePercent The cache size as a percent of available app memory. Should be in - * range of MIN_CACHE_SIZE_PERCENT(0.05) ~ MAX_CACHE_SIZE_PERCENT(0.8). + * range of MIN_CACHE_SIZE_PERCENT(0.05) ~ MAX_CACHE_SIZE_PERCENT(0.8). * @return An existing retained ImageCache object or a new one if one did not exist */ public static synchronized ImageCache getInstance(float memCacheSizePercent) { @@ -82,7 +80,6 @@ public class ImageCache implements MemoryManageable { return new ImageCache(memCacheSizePercent); } - /** * Returns an existing ImageCache, if it doesn't exist, a new one is created using * DEFAULT_CACHE_SIZE_PERCENT (0.1). @@ -96,8 +93,8 @@ public class ImageCache implements MemoryManageable { /** * Adds a bitmap to memory cache. * - * <p>If there is an existing bitmap only replace it if - * {@link ScaledBitmapInfo#needToReload(ScaledBitmapInfo)} is true. + * <p>If there is an existing bitmap only replace it if {@link + * ScaledBitmapInfo#needToReload(ScaledBitmapInfo)} is true. * * @param bitmapInfo The {@link ScaledBitmapInfo} object to store */ @@ -112,14 +109,25 @@ public class ImageCache implements MemoryManageable { if (old != null && !old.needToReload(bitmapInfo)) { mMemoryCache.put(key, old); if (DEBUG) { - Log.d(TAG, - "Kept original " + old + " in memory cache because it was larger than " - + bitmapInfo + "."); + Log.d( + TAG, + "Kept original " + + old + + " in memory cache because it was larger than " + + bitmapInfo + + "."); } } else { if (DEBUG) { - Log.d(TAG, "Add " + bitmapInfo + " to memory cache. Current size is " + - mMemoryCache.size() + " / " + mMemoryCache.maxSize() + " Kbytes"); + Log.d( + TAG, + "Add " + + bitmapInfo + + " to memory cache. Current size is " + + mMemoryCache.size() + + " / " + + mMemoryCache.maxSize() + + " Kbytes"); } } } @@ -158,19 +166,21 @@ public class ImageCache implements MemoryManageable { * Calculates the memory cache size based on a percentage of the max available VM memory. Eg. * setting percent to 0.2 would set the memory cache to one fifth of the available memory. * Throws {@link IllegalArgumentException} if percent is < 0.05 or > .8. memCacheSize is stored - * in kilobytes instead of bytes as this will eventually be passed to construct a LruCache - * which takes an int in its constructor. This value should be chosen carefully based on a - * number of factors Refer to the corresponding Android Training class for more discussion: + * in kilobytes instead of bytes as this will eventually be passed to construct a LruCache which + * takes an int in its constructor. This value should be chosen carefully based on a number of + * factors Refer to the corresponding Android Training class for more discussion: * http://developer.android.com/training/displaying-bitmaps/ * * @param percent Percent of available app memory to use to size memory cache. */ public static int calculateMemCacheSize(float percent) { if (percent < MIN_CACHE_SIZE_PERCENT || percent > MAX_CACHE_SIZE_PERCENT) { - throw new IllegalArgumentException("setMemCacheSizePercent - percent must be " - + "between 0.05 and 0.8 (inclusive)"); + throw new IllegalArgumentException( + "setMemCacheSizePercent - percent must be " + + "between 0.05 and 0.8 (inclusive)"); } - return Math.max(MIN_CACHE_SIZE_KBYTES, + return Math.max( + MIN_CACHE_SIZE_KBYTES, Math.round(percent * Runtime.getRuntime().maxMemory() / 1024)); } diff --git a/src/com/android/tv/util/ImageLoader.java b/src/com/android/tv/util/images/ImageLoader.java index 86bb94c1..e844e2ca 100644 --- a/src/com/android/tv/util/ImageLoader.java +++ b/src/com/android/tv/util/images/ImageLoader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.tv.util; +package com.android.tv.util.images; import android.content.Context; import android.graphics.Bitmap; @@ -30,10 +30,9 @@ import android.support.annotation.UiThread; import android.support.annotation.WorkerThread; import android.util.ArraySet; import android.util.Log; - import com.android.tv.R; -import com.android.tv.util.BitmapUtils.ScaledBitmapInfo; - +import com.android.tv.common.concurrent.NamedThreadFactory; +import com.android.tv.util.images.BitmapUtils.ScaledBitmapInfo; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; @@ -47,8 +46,8 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** - * This class wraps up completing some arbitrary long running work when loading a bitmap. It - * handles things like using a memory cache, running the work in a background thread. + * This class wraps up completing some arbitrary long running work when loading a bitmap. It handles + * things like using a memory cache, running the work in a background thread. */ public final class ImageLoader { private static final String TAG = "ImageLoader"; @@ -69,19 +68,23 @@ public final class ImageLoader { /** * An private {@link Executor} that can be used to execute tasks in parallel. * - * <p>{@code IMAGE_THREAD_POOL_EXECUTOR} setting are copied from {@link AsyncTask} - * Since we do a lot of concurrent image loading we can exhaust a thread pool. - * ImageLoader catches the error, and just leaves the image blank. - * However other tasks will fail and crash the application. + * <p>{@code IMAGE_THREAD_POOL_EXECUTOR} setting are copied from {@link AsyncTask} Since we do a + * lot of concurrent image loading we can exhaust a thread pool. ImageLoader catches the error, + * and just leaves the image blank. However other tasks will fail and crash the application. * * <p>Using a separate thread pool prevents image loading from causing other tasks to fail. */ private static final Executor IMAGE_THREAD_POOL_EXECUTOR; static { - ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, - MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, - sThreadFactory); + ThreadPoolExecutor threadPoolExecutor = + new ThreadPoolExecutor( + CORE_POOL_SIZE, + MAXIMUM_POOL_SIZE, + KEEP_ALIVE_SECONDS, + TimeUnit.SECONDS, + sPoolWorkQueue, + sThreadFactory); threadPoolExecutor.allowCoreThreadTimeOut(true); IMAGE_THREAD_POOL_EXECUTOR = threadPoolExecutor; } @@ -91,28 +94,26 @@ public final class ImageLoader { /** * Handles when image loading is finished. * - * <p>Use this to prevent leaking an Activity or other Context while image loading is - * still pending. When you extend this class you <strong>MUST NOT</strong> use a non static - * inner class, or the containing object will still be leaked. + * <p>Use this to prevent leaking an Activity or other Context while image loading is still + * pending. When you extend this class you <strong>MUST NOT</strong> use a non static inner + * class, or the containing object will still be leaked. */ @UiThread - public static abstract class ImageLoaderCallback<T> { + public abstract static class ImageLoaderCallback<T> { private final WeakReference<T> mWeakReference; /** * Creates an callback keeping a weak reference to {@code referent}. * - * <p> If the "referent" is no longer valid, it no longer makes sense to run the - * callback. The referent is the View, or Activity or whatever that actually needs to - * receive the Bitmap. If the referent has been GC, then no need to run the callback. + * <p>If the "referent" is no longer valid, it no longer makes sense to run the callback. + * The referent is the View, or Activity or whatever that actually needs to receive the + * Bitmap. If the referent has been GC, then no need to run the callback. */ public ImageLoaderCallback(T referent) { mWeakReference = new WeakReference<>(referent); } - /** - * Called when bitmap is loaded. - */ + /** Called when bitmap is loaded. */ private void onBitmapLoaded(@Nullable Bitmap bitmap) { T referent = mWeakReference.get(); if (referent != null) { @@ -122,9 +123,7 @@ public final class ImageLoader { } } - /** - * Called when bitmap is loaded if the weak reference is still valid. - */ + /** Called when bitmap is loaded if the weak reference is still valid. */ public abstract void onBitmapLoaded(T referent, @Nullable Bitmap bitmap); } @@ -134,62 +133,80 @@ public final class ImageLoader { * Preload a bitmap image into the cache. * * <p>Not to make heavy CPU load, AsyncTask.SERIAL_EXECUTOR is used for the image loading. + * * <p>This method is thread safe. */ - public static void prefetchBitmap(Context context, final String uriString, final int maxWidth, - final int maxHeight) { + public static void prefetchBitmap( + Context context, final String uriString, final int maxWidth, final int maxHeight) { if (DEBUG) Log.d(TAG, "prefetchBitmap() " + uriString); if (Looper.getMainLooper() == Looper.myLooper()) { doLoadBitmap(context, uriString, maxWidth, maxHeight, null, AsyncTask.SERIAL_EXECUTOR); } else { final Context appContext = context.getApplicationContext(); - getMainHandler().post(new Runnable() { - @Override - @MainThread - public void run() { - // Calling from the main thread prevents a ConcurrentModificationException - // in LoadBitmapTask.onPostExecute - doLoadBitmap(appContext, uriString, maxWidth, maxHeight, null, - AsyncTask.SERIAL_EXECUTOR); - } - }); + getMainHandler() + .post( + new Runnable() { + @Override + @MainThread + public void run() { + // Calling from the main thread prevents a + // ConcurrentModificationException + // in LoadBitmapTask.onPostExecute + doLoadBitmap( + appContext, + uriString, + maxWidth, + maxHeight, + null, + AsyncTask.SERIAL_EXECUTOR); + } + }); } } /** * Load a bitmap image with the cache using a ContentResolver. * - * <p><b>Note</b> that the callback will be called synchronously if the bitmap already is in - * the cache. + * <p><b>Note</b> that the callback will be called synchronously if the bitmap already is in the + * cache. * * @return {@code true} if the load is complete and the callback is executed. */ @UiThread - public static boolean loadBitmap(Context context, String uriString, - ImageLoaderCallback callback) { + public static boolean loadBitmap( + Context context, String uriString, ImageLoaderCallback callback) { return loadBitmap(context, uriString, Integer.MAX_VALUE, Integer.MAX_VALUE, callback); } /** * Load a bitmap image with the cache and resize it with given params. * - * <p><b>Note</b> that the callback will be called synchronously if the bitmap already is in - * the cache. + * <p><b>Note</b> that the callback will be called synchronously if the bitmap already is in the + * cache. * * @return {@code true} if the load is complete and the callback is executed. */ @UiThread - public static boolean loadBitmap(Context context, String uriString, int maxWidth, int maxHeight, + public static boolean loadBitmap( + Context context, + String uriString, + int maxWidth, + int maxHeight, ImageLoaderCallback callback) { if (DEBUG) { Log.d(TAG, "loadBitmap() " + uriString); } - return doLoadBitmap(context, uriString, maxWidth, maxHeight, callback, - IMAGE_THREAD_POOL_EXECUTOR); + return doLoadBitmap( + context, uriString, maxWidth, maxHeight, callback, IMAGE_THREAD_POOL_EXECUTOR); } - private static boolean doLoadBitmap(Context context, String uriString, - int maxWidth, int maxHeight, ImageLoaderCallback callback, Executor executor) { + private static boolean doLoadBitmap( + Context context, + String uriString, + int maxWidth, + int maxHeight, + ImageLoaderCallback callback, + Executor executor) { // Check the cache before creating a Task. The cache will be checked again in doLoadBitmap // but checking a cache is much cheaper than creating an new task. ImageCache imageCache = ImageCache.getInstance(); @@ -200,7 +217,9 @@ public final class ImageLoader { } return true; } - return doLoadBitmap(callback, executor, + return doLoadBitmap( + callback, + executor, new LoadBitmapFromUriTask(context, imageCache, uriString, maxWidth, maxHeight)); } @@ -219,12 +238,10 @@ public final class ImageLoader { return doLoadBitmap(callback, IMAGE_THREAD_POOL_EXECUTOR, loadBitmapTask); } - /** - * @return {@code true} if the load is complete and the callback is executed. - */ + /** @return {@code true} if the load is complete and the callback is executed. */ @UiThread - private static boolean doLoadBitmap(ImageLoaderCallback callback, Executor executor, - LoadBitmapTask loadBitmapTask) { + private static boolean doLoadBitmap( + ImageLoaderCallback callback, Executor executor, LoadBitmapTask loadBitmapTask) { ScaledBitmapInfo bitmapInfo = loadBitmapTask.getFromCache(); boolean needToReload = loadBitmapTask.isReloadNeeded(); if (bitmapInfo != null && !needToReload) { @@ -259,7 +276,7 @@ public final class ImageLoader { * * <p>Implement {@link #doGetBitmapInBackground} to do the actual loading. */ - public static abstract class LoadBitmapTask extends AsyncTask<Void, Void, ScaledBitmapInfo> { + public abstract static class LoadBitmapTask extends AsyncTask<Void, Void, ScaledBitmapInfo> { protected final Context mAppContext; protected final int mMaxWidth; protected final int mMaxHeight; @@ -273,24 +290,28 @@ public final class ImageLoader { */ private boolean isReloadNeeded() { ScaledBitmapInfo bitmapInfo = getFromCache(); - boolean needToReload = bitmapInfo != null && bitmapInfo - .needToReload(mMaxWidth, mMaxHeight); + boolean needToReload = + bitmapInfo != null && bitmapInfo.needToReload(mMaxWidth, mMaxHeight); if (DEBUG) { if (needToReload) { - Log.d(TAG, "Bitmap needs to be reloaded. {" - + "originalWidth=" + bitmapInfo.bitmap.getWidth() - + ", originalHeight=" + bitmapInfo.bitmap.getHeight() - + ", reqWidth=" + mMaxWidth - + ", reqHeight=" + mMaxHeight - + "}"); + Log.d( + TAG, + "Bitmap needs to be reloaded. {" + + "originalWidth=" + + bitmapInfo.bitmap.getWidth() + + ", originalHeight=" + + bitmapInfo.bitmap.getHeight() + + ", reqWidth=" + + mMaxWidth + + ", reqHeight=" + + mMaxHeight + + "}"); } } return needToReload; } - /** - * Checks if a reload would be needed if the results of other was available. - */ + /** Checks if a reload would be needed if the results of other was available. */ private boolean isReloadNeeded(LoadBitmapTask other) { return (other.mMaxHeight != Integer.MAX_VALUE && mMaxHeight >= other.mMaxHeight * 2) || (other.mMaxWidth != Integer.MAX_VALUE && mMaxWidth >= other.mMaxWidth * 2); @@ -301,11 +322,14 @@ public final class ImageLoader { return mImageCache.get(mKey); } - public LoadBitmapTask(Context context, ImageCache imageCache, String key, int maxHeight, - int maxWidth) { + public LoadBitmapTask( + Context context, ImageCache imageCache, String key, int maxHeight, int maxWidth) { if (maxWidth == 0 || maxHeight == 0) { throw new IllegalArgumentException( - "Image size should not be 0. {width=" + maxWidth + ", height=" + maxHeight + "Image size should not be 0. {width=" + + maxWidth + + ", height=" + + maxHeight + "}"); } mAppContext = context.getApplicationContext(); @@ -315,9 +339,7 @@ public final class ImageLoader { mMaxWidth = maxWidth; } - /** - * Loads the bitmap returning a possibly scaled down version. - */ + /** Loads the bitmap returning a possibly scaled down version. */ @Nullable @WorkerThread public abstract ScaledBitmapInfo doGetBitmapInBackground(); @@ -352,40 +374,48 @@ public final class ImageLoader { @Override public String toString() { - return this.getClass().getSimpleName() + "(" + mKey + " " + mMaxWidth + "x" + mMaxHeight + return this.getClass().getSimpleName() + + "(" + + mKey + + " " + + mMaxWidth + + "x" + + mMaxHeight + ")"; } } private static final class LoadBitmapFromUriTask extends LoadBitmapTask { - private LoadBitmapFromUriTask(Context context, ImageCache imageCache, String uriString, - int maxWidth, int maxHeight) { + private LoadBitmapFromUriTask( + Context context, + ImageCache imageCache, + String uriString, + int maxWidth, + int maxHeight) { super(context, imageCache, uriString, maxHeight, maxWidth); } @Override @Nullable public final ScaledBitmapInfo doGetBitmapInBackground() { - return BitmapUtils - .decodeSampledBitmapFromUriString(mAppContext, getKey(), mMaxWidth, mMaxHeight); + return BitmapUtils.decodeSampledBitmapFromUriString( + mAppContext, getKey(), mMaxWidth, mMaxHeight); } } - /** - * Loads and caches the logo for a given {@link TvInputInfo} - */ + /** Loads and caches the logo for a given {@link TvInputInfo} */ public static final class LoadTvInputLogoTask extends LoadBitmapTask { private final TvInputInfo mInfo; public LoadTvInputLogoTask(Context context, ImageCache cache, TvInputInfo info) { - super(context, + super( + context, cache, getTvInputLogoKey(info.getId()), context.getResources() .getDimensionPixelSize(R.dimen.channel_banner_input_logo_size), context.getResources() - .getDimensionPixelSize(R.dimen.channel_banner_input_logo_size) - ); + .getDimensionPixelSize(R.dimen.channel_banner_input_logo_size)); mInfo = info; } @@ -403,9 +433,7 @@ public final class ImageLoader { return BitmapUtils.createScaledBitmapInfo(getKey(), original, mMaxWidth, mMaxHeight); } - /** - * Returns key of TV input logo. - */ + /** Returns key of TV input logo. */ public static String getTvInputLogoKey(String inputId) { return inputId + "-logo"; } @@ -418,6 +446,5 @@ public final class ImageLoader { return sMainHandler; } - private ImageLoader() { - } + private ImageLoader() {} } |