Loading res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -11749,4 +11749,6 @@ <!-- Subtext for showing the option of RTT setting. [CHAR LIMIT=NONE] --> <string name="rtt_settings_always_visible"></string> <!-- Button label to stop casting on media device. [CHAR LIMIT=40 --> <string name="media_output_panel_stop_casting_button">Stop casting</string> </resources> src/com/android/settings/panel/MediaOutputPanel.java +82 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.settings.panel; import static androidx.lifecycle.Lifecycle.Event.ON_START; import static androidx.lifecycle.Lifecycle.Event.ON_STOP; import static com.android.settings.media.MediaOutputSlice.MEDIA_PACKAGE_NAME; import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_SLICE_URI; Loading @@ -35,9 +38,15 @@ import android.text.TextUtils; import android.util.Log; import androidx.core.graphics.drawable.IconCompat; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.Utils; import com.android.settingslib.media.InfoMediaDevice; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import java.util.ArrayList; import java.util.List; Loading @@ -49,13 +58,20 @@ import java.util.List; * Displays Media output item * </p> */ public class MediaOutputPanel implements PanelContent { public class MediaOutputPanel implements PanelContent, LocalMediaManager.DeviceCallback, LifecycleObserver { private static final String TAG = "MediaOutputPanel"; private final Context mContext; private final String mPackageName; private PanelCustomizedButtonCallback mCallback; private boolean mIsCustomizedButtonUsed = true; @VisibleForTesting LocalMediaManager mLocalMediaManager; private MediaSessionManager mMediaSessionManager; private MediaController mMediaController; Loading @@ -65,8 +81,9 @@ public class MediaOutputPanel implements PanelContent { private MediaOutputPanel(Context context, String packageName) { mContext = context.getApplicationContext(); mPackageName = packageName; if (mPackageName != null) { mPackageName = TextUtils.isEmpty(packageName) ? "" : packageName; if (!TextUtils.isEmpty(mPackageName)) { mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class); for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) { if (TextUtils.equals(controller.getPackageName(), mPackageName)) { Loading @@ -75,6 +92,7 @@ public class MediaOutputPanel implements PanelContent { } } } if (mMediaController == null) { Log.e(TAG, "Unable to find " + mPackageName + " media controller"); } Loading Loading @@ -156,8 +174,69 @@ public class MediaOutputPanel implements PanelContent { return null; } @Override public boolean isCustomizedButtonUsed() { return mIsCustomizedButtonUsed; } @Override public CharSequence getCustomButtonTitle() { return mContext.getText(R.string.media_output_panel_stop_casting_button); } @Override public void onClickCustomizedButton() { } @Override public void registerCallback(PanelCustomizedButtonCallback callback) { mCallback = callback; } @Override public int getMetricsCategory() { return SettingsEnums.PANEL_MEDIA_OUTPUT; } @Override public void onSelectedDeviceStateChanged(MediaDevice device, int state) { dispatchCustomButtonStateChanged(); } @Override public void onDeviceListUpdate(List<MediaDevice> devices) { dispatchCustomButtonStateChanged(); } @Override public void onDeviceAttributesChanged() { dispatchCustomButtonStateChanged(); } private void dispatchCustomButtonStateChanged() { hideCustomButtonIfNecessary(); if (mCallback != null) { mCallback.onCustomizedButtonStateChanged(); } } private void hideCustomButtonIfNecessary() { final MediaDevice device = mLocalMediaManager.getCurrentConnectedDevice(); mIsCustomizedButtonUsed = device instanceof InfoMediaDevice; } @OnLifecycleEvent(ON_START) public void onStart() { if (mLocalMediaManager == null) { mLocalMediaManager = new LocalMediaManager(mContext, mPackageName, null); } mLocalMediaManager.registerCallback(this); mLocalMediaManager.startScan(); } @OnLifecycleEvent(ON_STOP) public void onStop() { mLocalMediaManager.unregisterCallback(this); mLocalMediaManager.stopScan(); } } src/com/android/settings/panel/PanelContent.java +27 −0 Original line number Diff line number Diff line Loading @@ -74,4 +74,31 @@ public interface PanelContent extends Instrumentable { default Intent getHeaderIconIntent() { return null; } /** * @return {@code true} to enable custom button to replace see more button, * {@code false} otherwise. */ default boolean isCustomizedButtonUsed() { return false; } /** * @return a string for the title of the custom button. */ default CharSequence getCustomButtonTitle() { return null; } /** * Implement the click event for custom button. */ default void onClickCustomizedButton() {} /** * Register to start receiving callbacks for custom button events. * * @param callback the callback to add. */ default void registerCallback(PanelCustomizedButtonCallback callback) {} } src/com/android/settings/panel/PanelCustomizedButtonCallback.java 0 → 100644 +29 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.settings.panel; /** * PanelCustomizedButtonCallback provides a callback interface for {@link PanelFragment} to receive * events from {@link PanelContent}. */ public interface PanelCustomizedButtonCallback { /** * It will be called when customized button state is changed. For example, custom button * would be hidden for specific behavior. */ void onCustomizedButtonStateChanged(); } src/com/android/settings/panel/PanelFragment.java +34 −5 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import androidx.annotation.Nullable; import androidx.core.graphics.drawable.IconCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LiveData; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; Loading @@ -53,6 +54,7 @@ import com.android.settings.R; import com.android.settings.overlay.FeatureFactory; import com.android.settings.panel.PanelLoggingContract.PanelClosedKeys; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.utils.ThreadUtils; import com.google.android.setupdesign.DividerItemDecoration; Loading Loading @@ -183,6 +185,11 @@ public class PanelFragment extends Fragment { activity.finish(); } mPanel.registerCallback(new LocalPanelCallback()); if (mPanel instanceof LifecycleObserver) { getLifecycle().addObserver((LifecycleObserver) mPanel); } mMetricsProvider = FeatureFactory.getFactory(activity).getMetricsFeatureProvider(); mPanelSlices.setLayoutManager(new LinearLayoutManager((activity))); Loading @@ -208,8 +215,15 @@ public class PanelFragment extends Fragment { mSeeMoreButton.setOnClickListener(getSeeMoreListener()); mDoneButton.setOnClickListener(getCloseListener()); // If getSeeMoreIntent() is null, hide the mSeeMoreButton. if (mPanel.getSeeMoreIntent() == null) { if (mPanel.isCustomizedButtonUsed()) { final CharSequence customTitle = mPanel.getCustomButtonTitle(); if (TextUtils.isEmpty(customTitle)) { mSeeMoreButton.setVisibility(View.GONE); } else { mSeeMoreButton.setText(customTitle); } } else if (mPanel.getSeeMoreIntent() == null) { // If getSeeMoreIntent() is null hide the mSeeMoreButton. mSeeMoreButton.setVisibility(View.GONE); } Loading Loading @@ -371,9 +385,13 @@ public class PanelFragment extends Fragment { View.OnClickListener getSeeMoreListener() { return (v) -> { mPanelClosedKey = PanelClosedKeys.KEY_SEE_MORE; if (mPanel.isCustomizedButtonUsed()) { mPanel.onClickCustomizedButton(); } else { final FragmentActivity activity = getActivity(); activity.startActivityForResult(mPanel.getSeeMoreIntent(), 0); activity.finish(); } }; } Loading @@ -392,4 +410,15 @@ public class PanelFragment extends Fragment { activity.startActivity(mPanel.getHeaderIconIntent()); }; } class LocalPanelCallback implements PanelCustomizedButtonCallback { @Override public void onCustomizedButtonStateChanged() { ThreadUtils.postOnMainThread(() -> { mSeeMoreButton.setVisibility( mPanel.isCustomizedButtonUsed() ? View.VISIBLE : View.GONE); }); } } } Loading
res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -11749,4 +11749,6 @@ <!-- Subtext for showing the option of RTT setting. [CHAR LIMIT=NONE] --> <string name="rtt_settings_always_visible"></string> <!-- Button label to stop casting on media device. [CHAR LIMIT=40 --> <string name="media_output_panel_stop_casting_button">Stop casting</string> </resources>
src/com/android/settings/panel/MediaOutputPanel.java +82 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.settings.panel; import static androidx.lifecycle.Lifecycle.Event.ON_START; import static androidx.lifecycle.Lifecycle.Event.ON_STOP; import static com.android.settings.media.MediaOutputSlice.MEDIA_PACKAGE_NAME; import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_SLICE_URI; Loading @@ -35,9 +38,15 @@ import android.text.TextUtils; import android.util.Log; import androidx.core.graphics.drawable.IconCompat; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.Utils; import com.android.settingslib.media.InfoMediaDevice; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import java.util.ArrayList; import java.util.List; Loading @@ -49,13 +58,20 @@ import java.util.List; * Displays Media output item * </p> */ public class MediaOutputPanel implements PanelContent { public class MediaOutputPanel implements PanelContent, LocalMediaManager.DeviceCallback, LifecycleObserver { private static final String TAG = "MediaOutputPanel"; private final Context mContext; private final String mPackageName; private PanelCustomizedButtonCallback mCallback; private boolean mIsCustomizedButtonUsed = true; @VisibleForTesting LocalMediaManager mLocalMediaManager; private MediaSessionManager mMediaSessionManager; private MediaController mMediaController; Loading @@ -65,8 +81,9 @@ public class MediaOutputPanel implements PanelContent { private MediaOutputPanel(Context context, String packageName) { mContext = context.getApplicationContext(); mPackageName = packageName; if (mPackageName != null) { mPackageName = TextUtils.isEmpty(packageName) ? "" : packageName; if (!TextUtils.isEmpty(mPackageName)) { mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class); for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) { if (TextUtils.equals(controller.getPackageName(), mPackageName)) { Loading @@ -75,6 +92,7 @@ public class MediaOutputPanel implements PanelContent { } } } if (mMediaController == null) { Log.e(TAG, "Unable to find " + mPackageName + " media controller"); } Loading Loading @@ -156,8 +174,69 @@ public class MediaOutputPanel implements PanelContent { return null; } @Override public boolean isCustomizedButtonUsed() { return mIsCustomizedButtonUsed; } @Override public CharSequence getCustomButtonTitle() { return mContext.getText(R.string.media_output_panel_stop_casting_button); } @Override public void onClickCustomizedButton() { } @Override public void registerCallback(PanelCustomizedButtonCallback callback) { mCallback = callback; } @Override public int getMetricsCategory() { return SettingsEnums.PANEL_MEDIA_OUTPUT; } @Override public void onSelectedDeviceStateChanged(MediaDevice device, int state) { dispatchCustomButtonStateChanged(); } @Override public void onDeviceListUpdate(List<MediaDevice> devices) { dispatchCustomButtonStateChanged(); } @Override public void onDeviceAttributesChanged() { dispatchCustomButtonStateChanged(); } private void dispatchCustomButtonStateChanged() { hideCustomButtonIfNecessary(); if (mCallback != null) { mCallback.onCustomizedButtonStateChanged(); } } private void hideCustomButtonIfNecessary() { final MediaDevice device = mLocalMediaManager.getCurrentConnectedDevice(); mIsCustomizedButtonUsed = device instanceof InfoMediaDevice; } @OnLifecycleEvent(ON_START) public void onStart() { if (mLocalMediaManager == null) { mLocalMediaManager = new LocalMediaManager(mContext, mPackageName, null); } mLocalMediaManager.registerCallback(this); mLocalMediaManager.startScan(); } @OnLifecycleEvent(ON_STOP) public void onStop() { mLocalMediaManager.unregisterCallback(this); mLocalMediaManager.stopScan(); } }
src/com/android/settings/panel/PanelContent.java +27 −0 Original line number Diff line number Diff line Loading @@ -74,4 +74,31 @@ public interface PanelContent extends Instrumentable { default Intent getHeaderIconIntent() { return null; } /** * @return {@code true} to enable custom button to replace see more button, * {@code false} otherwise. */ default boolean isCustomizedButtonUsed() { return false; } /** * @return a string for the title of the custom button. */ default CharSequence getCustomButtonTitle() { return null; } /** * Implement the click event for custom button. */ default void onClickCustomizedButton() {} /** * Register to start receiving callbacks for custom button events. * * @param callback the callback to add. */ default void registerCallback(PanelCustomizedButtonCallback callback) {} }
src/com/android/settings/panel/PanelCustomizedButtonCallback.java 0 → 100644 +29 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.settings.panel; /** * PanelCustomizedButtonCallback provides a callback interface for {@link PanelFragment} to receive * events from {@link PanelContent}. */ public interface PanelCustomizedButtonCallback { /** * It will be called when customized button state is changed. For example, custom button * would be hidden for specific behavior. */ void onCustomizedButtonStateChanged(); }
src/com/android/settings/panel/PanelFragment.java +34 −5 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import androidx.annotation.Nullable; import androidx.core.graphics.drawable.IconCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LiveData; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; Loading @@ -53,6 +54,7 @@ import com.android.settings.R; import com.android.settings.overlay.FeatureFactory; import com.android.settings.panel.PanelLoggingContract.PanelClosedKeys; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.utils.ThreadUtils; import com.google.android.setupdesign.DividerItemDecoration; Loading Loading @@ -183,6 +185,11 @@ public class PanelFragment extends Fragment { activity.finish(); } mPanel.registerCallback(new LocalPanelCallback()); if (mPanel instanceof LifecycleObserver) { getLifecycle().addObserver((LifecycleObserver) mPanel); } mMetricsProvider = FeatureFactory.getFactory(activity).getMetricsFeatureProvider(); mPanelSlices.setLayoutManager(new LinearLayoutManager((activity))); Loading @@ -208,8 +215,15 @@ public class PanelFragment extends Fragment { mSeeMoreButton.setOnClickListener(getSeeMoreListener()); mDoneButton.setOnClickListener(getCloseListener()); // If getSeeMoreIntent() is null, hide the mSeeMoreButton. if (mPanel.getSeeMoreIntent() == null) { if (mPanel.isCustomizedButtonUsed()) { final CharSequence customTitle = mPanel.getCustomButtonTitle(); if (TextUtils.isEmpty(customTitle)) { mSeeMoreButton.setVisibility(View.GONE); } else { mSeeMoreButton.setText(customTitle); } } else if (mPanel.getSeeMoreIntent() == null) { // If getSeeMoreIntent() is null hide the mSeeMoreButton. mSeeMoreButton.setVisibility(View.GONE); } Loading Loading @@ -371,9 +385,13 @@ public class PanelFragment extends Fragment { View.OnClickListener getSeeMoreListener() { return (v) -> { mPanelClosedKey = PanelClosedKeys.KEY_SEE_MORE; if (mPanel.isCustomizedButtonUsed()) { mPanel.onClickCustomizedButton(); } else { final FragmentActivity activity = getActivity(); activity.startActivityForResult(mPanel.getSeeMoreIntent(), 0); activity.finish(); } }; } Loading @@ -392,4 +410,15 @@ public class PanelFragment extends Fragment { activity.startActivity(mPanel.getHeaderIconIntent()); }; } class LocalPanelCallback implements PanelCustomizedButtonCallback { @Override public void onCustomizedButtonStateChanged() { ThreadUtils.postOnMainThread(() -> { mSeeMoreButton.setVisibility( mPanel.isCustomizedButtonUsed() ? View.VISIBLE : View.GONE); }); } } }