blob: 5df31cbfe52b03ca1d4a597005064f07177f6b65 [file] [log] [blame]
/*
* Copyright (C) 2019 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.car.radio.service;
import android.os.RemoteException;
import android.util.IndentingPrintWriter;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import com.android.car.broadcastradio.support.Program;
import com.android.car.radio.SkipMode;
import com.android.car.radio.util.Log;
import java.util.List;
/**
* Helper class used to keep track of which station should be toggled next (or prev), based on
* {@link SkipMode}.
*/
final class SkipController {
private static final String TAG = SkipController.class.getSimpleName();
private final Object mLock = new Object();
private final IRadioAppService.Stub mService;
@GuardedBy("mLock")
private List<Program> mFavorites;
@GuardedBy("mLock")
private int mCurrentIndex;
@GuardedBy("mLock")
private SkipMode mSkipMode;
SkipController(@NonNull IRadioAppService.Stub service,
@NonNull LiveData<List<Program>> favorites, @NonNull SkipMode initialMode) {
mService = service;
mSkipMode = initialMode;
Log.v(TAG, "Initial mode: %s", initialMode);
// TODO(b/137647889): not really working because they're changed in a different process.
// As such, the changes are only effective after the radio service restarts - that's
// not ideal, but it's better than nothing :-)
// Long term, we need to provide a way to sync them...
favorites.observeForever(this::onFavoritesChanged);
}
void setSkipMode(@NonNull SkipMode mode) {
Log.d(TAG, "setSkipMode(%s)", mode);
synchronized (mLock) {
this.mSkipMode = mode;
}
}
void skip(boolean forward, ITuneCallback callback) throws RemoteException {
Log.d(TAG, "skip(%s, %s)", mSkipMode, forward);
Program program = null;
synchronized (mLock) {
if (mSkipMode == SkipMode.FAVORITES || mSkipMode == SkipMode.BROWSE) {
program = getFavoriteLocked(forward);
if (program == null) {
Log.d(TAG, "skip(%s): no favorites, seeking instead", forward);
}
}
}
if (program != null) {
Log.d(TAG, "skip(%s): changing to %s", forward, program.getName());
mService.tune(program.getSelector(), callback);
} else {
mService.seek(forward, callback);
}
}
private void onFavoritesChanged(List<Program> favorites) {
Log.v(TAG, "onFavoritesChanged(): %s", favorites);
synchronized (this) {
mFavorites = favorites;
// TODO(b/137647889): try to preserve currentIndex, either pointing to the same station,
// or the closest one
mCurrentIndex = 0;
}
}
@Nullable
private Program getFavoriteLocked(boolean next) {
if (mFavorites == null || mFavorites.isEmpty()) return null;
// TODO(b/137647889): to keep it simple, we're only interacting through explicit calls
// to prev/next, but ideally it should also take in account the current station.
// For example, say the favorites are 4, 8, 15, 16, 23, 42 and user skipped from
// 15 to 16 but later manually tuned to 5. In this case, if the user skips again we'll
// return 23 (next index), but ideally it would be 8 (i.e, next favorite whose value
// is higher than 5)
if (next) {
mCurrentIndex++;
if (mCurrentIndex >= mFavorites.size()) {
mCurrentIndex = 0;
}
} else {
mCurrentIndex--;
if (mCurrentIndex < 0) {
mCurrentIndex = mFavorites.size() - 1;
}
}
Program program = mFavorites.get(mCurrentIndex);
Log.v(TAG, "getting favorite #" + mCurrentIndex + ": " + program.getName());
return program;
}
void dump(IndentingPrintWriter pw) {
pw.println("SkipController");
pw.increaseIndent();
synchronized (mLock) {
pw.printf("mode: %s\n", mSkipMode);
pw.printf("current index: %d\n", mCurrentIndex);
if (mFavorites == null || mFavorites.isEmpty()) {
pw.println("no favorites");
} else {
pw.printf("%d favorites:\n", mFavorites.size());
pw.increaseIndent();
for (int i = 0; i < mFavorites.size(); i++) {
pw.printf("Favorite[%d]: %s ", i, mFavorites.get(i).getName());
if (i == mCurrentIndex) {
pw.printf(" is current");
}
pw.println();
}
pw.decreaseIndent();
}
}
pw.decreaseIndent();
}
}