blob: 85ddc387daad1fb6982bda89ec5deb2305e835cf [file] [log] [blame]
/*
* Copyright (C) 2021 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.media.common.source;
import static android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_BROWSE;
import android.app.Application;
import android.car.media.CarMediaIntents;
import android.content.ComponentName;
import android.content.Intent;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import java.util.Objects;
/**
* This class helps different Media "Hosts" (CarMediaApp, CarRadioApp...) pretend they are a single
* app, by automatically launching the intent needed to see a newly activated media service in its
* proper host. For example when the user is displaying the Radio and activates by voice the local
* media player, that service becomes visible in the CarMediaApp. <p/>
*
* An activity should only use a single instance of this class, and should call
* {@link #setLaunchedMediaSource} when it is resumed with a new intent.
*/
public class MediaTrampolineHelper implements LifecycleObserver {
private static final String TAG = "MediaTrampolineHlp";
private final FragmentActivity mActivity;
private final MediaSourceViewModel mMediaSourceVM;
private MediaSource mLaunchedSource;
private boolean mIsResumed;
public MediaTrampolineHelper(@NonNull FragmentActivity activity) {
mActivity = activity;
Application app = mActivity.getApplication();
mMediaSourceVM = MediaSourceViewModel.get(app, MEDIA_SOURCE_MODE_BROWSE);
mActivity.getLifecycle().addObserver(this);
mMediaSourceVM.getPrimaryMediaSource().observe(mActivity, source -> {
if (mIsResumed) {
Log.i(TAG, "New media source while resumed: " + source);
maybeTrampoline(source);
}
});
}
/**
* Sets the media source that should be 1) recorded as primary, and 2) used as baseline when
* the helper detects a new media source and decides to trampoline to it.
* Should be called when the activity is resumed with a new intent (and the intent should be
* flagged as consumed).
*/
public void setLaunchedMediaSource(@Nullable ComponentName launchedSourceComp) {
if (launchedSourceComp != null) {
mLaunchedSource = MediaSource.create(mActivity, launchedSourceComp);
}
if (mLaunchedSource != null) {
// Since the activity is being explicitly launched, make sure its source is primary.
mMediaSourceVM.setPrimaryMediaSource(mLaunchedSource, MEDIA_SOURCE_MODE_BROWSE);
} else {
// Might happen if there's no media source at all on the system ?!
Log.e(TAG, "Null mLaunchedSource!!!");
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
MediaSource currentSource = mMediaSourceVM.getPrimaryMediaSource().getValue();
Log.d(TAG, "Launched: " + mLaunchedSource + " current: " + currentSource);
}
}
/***/
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
mIsResumed = true;
maybeTrampoline(mMediaSourceVM.getPrimaryMediaSource().getValue());
}
/***/
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
mIsResumed = false;
}
private void maybeTrampoline(MediaSource latestSource) {
if (!Objects.equals(mLaunchedSource, latestSource)) {
// Always go through the trampoline activity to keep the dispatching logic there.
if (Log.isLoggable(TAG, Log.INFO)) {
Log.i(TAG, "Launching: " + latestSource + " old: " + mLaunchedSource);
}
Intent intent = new Intent(CarMediaIntents.ACTION_MEDIA_TEMPLATE);
if (latestSource != null) {
ComponentName component = latestSource.getBrowseServiceComponentName();
intent.putExtra(CarMediaIntents.EXTRA_MEDIA_COMPONENT, component.flattenToString());
} else if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "latestSource is null in onResume");
}
mActivity.startActivity(intent);
}
}
}