/* * Copyright (C) 2024 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.systemui.tv.media.settings; import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; import static android.app.slice.Slice.HINT_PARTIAL; import static com.android.tv.twopanelsettings.slices.SlicesConstants.EXTRA_PREFERENCE_INFO_STATUS; import static com.android.tv.twopanelsettings.slices.SlicesConstants.EXTRA_PREFERENCE_KEY; import static com.android.tv.twopanelsettings.slices.SlicesConstants.EXTRA_SLICE_FOLLOWUP; import android.app.Activity; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.app.tvsettings.TvSettingsEnums; import android.content.ContentProviderClient; import android.content.Intent; import android.content.IntentSender; import android.database.ContentObserver; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.IntentSenderRequest; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.lifecycle.Observer; import androidx.preference.Preference; import androidx.preference.PreferenceDialogFragmentCompat; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import androidx.preference.TwoStatePreference; import androidx.recyclerview.widget.RecyclerView; import com.android.systemui.tv.media.FadingEdgeUtil; import com.android.systemui.tv.res.R; import com.android.tv.twopanelsettings.TwoPanelSettingsFragment.SliceFragmentCallback; import com.android.tv.twopanelsettings.slices.EmbeddedSlicePreference; import com.android.tv.twopanelsettings.slices.HasCustomContentDescription; import com.android.tv.twopanelsettings.slices.HasSliceAction; import com.android.tv.twopanelsettings.slices.HasSliceUri; import com.android.tv.twopanelsettings.slices.SettingsPreferenceFragment; import com.android.tv.twopanelsettings.slices.SlicePreference; import com.android.tv.twopanelsettings.slices.SliceRadioPreference; import com.android.tv.twopanelsettings.slices.SliceSeekbarPreference; import com.android.tv.twopanelsettings.slices.SlicesConstants; import com.android.tv.twopanelsettings.slices.ContextSingleton; import com.android.tv.twopanelsettings.slices.compat.Slice; import com.android.tv.twopanelsettings.slices.compat.SliceItem; import com.android.tv.twopanelsettings.slices.compat.widget.ListContent; import com.android.tv.twopanelsettings.slices.compat.widget.SliceContent; import java.util.ArrayList; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * A screen presenting a slice in TV settings. * Forked from {@link com.android.tv.twopanelsettings.slices.SliceFragment}. */ @Keep public class SliceFragment extends SettingsPreferenceFragment implements Observer, SliceFragmentCallback, PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { private static final String TAG = "SliceFragment"; private static final boolean DEBUG = false; public static final String TAG_SCREEN_SUBTITLE = "TAG_SCREEN_SUBTITLE"; // Keys for saving state private static final String KEY_PREFERENCE_FOLLOWUP_INTENT = "key_preference_followup_intent"; private static final String KEY_PREFERENCE_FOLLOWUP_RESULT_CODE = "key_preference_followup_result_code"; private static final String KEY_SCREEN_TITLE = "key_screen_title"; private static final String KEY_SCREEN_SUBTITLE = "key_screen_subtitle"; private static final String KEY_LAST_PREFERENCE = "key_last_preference"; private static final String KEY_URI_STRING = "key_uri_string"; private Slice mSlice; private String mUriString = null; private int mCurrentPageId; private CharSequence mScreenTitle; private CharSequence mScreenSubtitle; private PendingIntent mPreferenceFollowupIntent; private int mFollowupPendingIntentResultCode; private Intent mFollowupPendingIntentExtras; private Intent mFollowupPendingIntentExtrasCopy; private String mLastFocusedPreferenceKey; private final ActivityResultLauncher mActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartIntentSenderForResult(), new ActivityResultCallback<>() { @Override public void onActivityResult(ActivityResult result) { Intent data = result.getData(); mFollowupPendingIntentExtras = data; mFollowupPendingIntentExtrasCopy = data == null ? null : new Intent( data); mFollowupPendingIntentResultCode = result.getResultCode(); } }); private final ContentObserver mContentObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange, Uri uri) { handleUri(uri); super.onChange(selfChange, uri); } }; @Override public void onCreate(Bundle savedInstanceState) { mUriString = getArguments().getString(SlicesConstants.TAG_TARGET_URI); if (!TextUtils.isEmpty(mUriString)) { ContextSingleton.getInstance().grantFullAccess(getContext(), Uri.parse(mUriString)); } if (TextUtils.isEmpty(mScreenTitle)) { mScreenTitle = getArguments().getCharSequence(SlicesConstants.TAG_SCREEN_TITLE, ""); } if (TextUtils.isEmpty(mScreenSubtitle)) { mScreenSubtitle = getArguments().getCharSequence(TAG_SCREEN_SUBTITLE, ""); } super.onCreate(savedInstanceState); getPreferenceManager().setPreferenceComparisonCallback( new PreferenceManager.SimplePreferenceComparisonCallback() { @Override public boolean arePreferenceContentsTheSame(Preference preference1, Preference preference2) { // Should only check for the default SlicePreference objects, and ignore // other instances of slice reference classes since they all override // Preference.onBindViewHolder(PreferenceViewHolder) return preference1.getClass() == SlicePreference.class && super.arePreferenceContentsTheSame(preference1, preference2); } }); } @Override public final boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) { if (DEBUG) Log.d(TAG, "onPreferenceStartFragment"); if (pref.getFragment() != null) { if (pref instanceof SlicePreference) { SlicePreference slicePref = (SlicePreference) pref; if (slicePref.getUri() == null || !isUriValid(slicePref.getUri())) { return false; } Bundle b = pref.getExtras(); b.putString(SlicesConstants.TAG_TARGET_URI, slicePref.getUri()); b.putCharSequence(SlicesConstants.TAG_SCREEN_TITLE, slicePref.getTitle()); if (DEBUG) Log.d(TAG, "TAG_TARGET_URI: " + slicePref.getUri() + ", TAG_SCREEN_TITLE: " + slicePref.getTitle()); } } final Fragment f = Fragment.instantiate(getActivity(), pref.getFragment(), pref.getExtras()); f.setTargetFragment(caller, 0); if (f instanceof PreferenceFragmentCompat || f instanceof PreferenceDialogFragmentCompat) { startPreferenceFragment(f); } return true; } public void startPreferenceFragment(@NonNull Fragment fragment) { if (DEBUG) Log.d(TAG, "startPreferenceFragment"); getParentFragmentManager().beginTransaction() .replace(R.id.media_output_fragment, fragment) .addToBackStack(null) .commit(); } @Override public void onResume() { this.setTitle(mScreenTitle); this.setSubtitle(mScreenSubtitle); showProgressBar(); if (!TextUtils.isEmpty(mUriString)) { ContextSingleton.getInstance() .addSliceObserver(getActivity(), Uri.parse(mUriString), this); } super.onResume(); if (!TextUtils.isEmpty(mUriString)) { getContext().getContentResolver().registerContentObserver( SlicePreferencesUtil.getStatusPath(mUriString), false, mContentObserver); } fireFollowupPendingIntent(); } private void fireFollowupPendingIntent() { if (mFollowupPendingIntentExtras == null) { return; } // If there is followup pendingIntent returned from initial activity, send it. // Otherwise send the followup pendingIntent provided by slice api. Parcelable followupPendingIntent; try { followupPendingIntent = mFollowupPendingIntentExtrasCopy.getParcelableExtra( EXTRA_SLICE_FOLLOWUP); } catch (Throwable ex) { // unable to parse, the Intent has custom Parcelable, fallback followupPendingIntent = null; } if (followupPendingIntent instanceof PendingIntent) { try { ((PendingIntent) followupPendingIntent).send(); } catch (CanceledException e) { Log.e(TAG, "Followup PendingIntent for slice cannot be sent", e); } } else { if (mPreferenceFollowupIntent == null) { return; } try { mPreferenceFollowupIntent.send(getContext(), mFollowupPendingIntentResultCode, mFollowupPendingIntentExtras); } catch (CanceledException e) { Log.e(TAG, "Followup PendingIntent for slice cannot be sent", e); } mPreferenceFollowupIntent = null; } } @Override public void onPause() { super.onPause(); hideProgressBar(); getContext().getContentResolver().unregisterContentObserver(mContentObserver); if (!TextUtils.isEmpty(mUriString)) { ContextSingleton.getInstance() .removeSliceObserver(getActivity(), Uri.parse(mUriString), this); } } @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { PreferenceScreen preferenceScreen = getPreferenceManager() .createPreferenceScreen(getContext()); setPreferenceScreen(preferenceScreen); } private boolean isUriValid(String uri) { if (uri == null) { return false; } ContentProviderClient client = getContext().getContentResolver().acquireContentProviderClient(Uri.parse(uri)); if (client != null) { client.close(); return true; } else { return false; } } private void update() { PreferenceScreen preferenceScreen = getPreferenceManager().getPreferenceScreen(); if (preferenceScreen == null) { return; } List items = new ListContent(mSlice).getRowItems(); if (items.isEmpty()) { return; } SliceItem redirectSliceItem = SlicePreferencesUtil.getRedirectSlice(items); String redirectSlice = null; if (redirectSliceItem != null) { SlicePreferencesUtil.Data data = SlicePreferencesUtil.extract(redirectSliceItem); CharSequence title = SlicePreferencesUtil.getText(data.mTitleItem); if (!TextUtils.isEmpty(title)) { redirectSlice = title.toString(); } } if (isUriValid(redirectSlice)) { ContextSingleton.getInstance() .removeSliceObserver(getActivity(), Uri.parse(mUriString), this); getContext().getContentResolver().unregisterContentObserver(mContentObserver); mUriString = redirectSlice; ContextSingleton.getInstance() .addSliceObserver(getActivity(), Uri.parse(mUriString), this); getContext().getContentResolver().registerContentObserver( SlicePreferencesUtil.getStatusPath(mUriString), false, mContentObserver); } SliceItem screenTitleItem = SlicePreferencesUtil.getScreenTitleItem(items); if (screenTitleItem == null) { setTitle(mScreenTitle); setSubtitle(mScreenSubtitle); } else { SlicePreferencesUtil.Data data = SlicePreferencesUtil.extract(screenTitleItem); mCurrentPageId = SlicePreferencesUtil.getPageId(screenTitleItem); CharSequence title = SlicePreferencesUtil.getText(data.mTitleItem); if (!TextUtils.isEmpty(title)) { mScreenTitle = title; } setTitle(mScreenTitle); CharSequence subtitle = SlicePreferencesUtil.getText(data.mSubtitleItem); if (!TextUtils.isEmpty(subtitle)) { mScreenSubtitle = subtitle; } setSubtitle(subtitle); } SliceItem focusedPrefItem = SlicePreferencesUtil.getFocusedPreferenceItem(items); CharSequence defaultFocusedKey = null; if (focusedPrefItem != null) { SlicePreferencesUtil.Data data = SlicePreferencesUtil.extract(focusedPrefItem); CharSequence title = SlicePreferencesUtil.getText(data.mTitleItem); if (!TextUtils.isEmpty(title)) { defaultFocusedKey = title; } } List newPrefs = new ArrayList<>(); for (SliceContent contentItem : items) { SliceItem item = contentItem.getSliceItem(); if (SlicesConstants.TYPE_PREFERENCE.equals(item.getSubType()) || SlicesConstants.TYPE_PREFERENCE_CATEGORY.equals(item.getSubType()) || SlicesConstants.TYPE_PREFERENCE_EMBEDDED_PLACEHOLDER.equals( item.getSubType())) { Preference preference = SlicePreferencesUtil.getPreference( item, getContext(), getClass().getCanonicalName()); if (preference != null) { // Listen to changes of the seekbar. if (preference instanceof SeekbarSlicePreference) { SeekbarSlicePreference seekbarPreference = (SeekbarSlicePreference) preference; seekbarPreference.setOnSeekbarChangedListener( new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { onSeekbarPreferenceValueChanged(seekbarPreference, progress); } @Override public void onStartTrackingTouch(SeekBar seekBar) { // NOOP } @Override public void onStopTrackingTouch(SeekBar seekBar) { // NOOP } }); } newPrefs.add(preference); } } } updatePreferenceScreen(preferenceScreen, newPrefs); if (defaultFocusedKey != null) { scrollToPreference(defaultFocusedKey.toString()); } else if (mLastFocusedPreferenceKey != null) { scrollToPreference(mLastFocusedPreferenceKey); } } private void back() { if (DEBUG) Log.d(TAG, "back"); getParentFragmentManager().popBackStack(); } private void updatePreferenceScreen(PreferenceScreen screen, List newPrefs) { // Remove all the preferences in the screen that satisfy such three cases: // (a) Preference without key // (b) Preference with key which does not appear in the new list. // (c) Preference with key which does appear in the new list, but the preference has changed // ability to handle slices and needs to be replaced instead of re-used. int index = 0; IdentityHashMap newToOld = new IdentityHashMap<>(); while (index < screen.getPreferenceCount()) { boolean needToRemoveCurrentPref = true; Preference oldPref = screen.getPreference(index); for (Preference newPref : newPrefs) { if (isSamePreference(oldPref, newPref)) { needToRemoveCurrentPref = false; newToOld.put(newPref, oldPref); break; } } if (needToRemoveCurrentPref) { screen.removePreference(oldPref); } else { index++; } } Map twoStatePreferenceIsCheckedByOrder = new HashMap<>(); for (int i = 0; i < newPrefs.size(); i++) { if (newPrefs.get(i) instanceof TwoStatePreference) { twoStatePreferenceIsCheckedByOrder.put( i, ((TwoStatePreference) newPrefs.get(i)).isChecked()); } } //Iterate the new preferences list and give each preference a correct order for (int i = 0; i < newPrefs.size(); i++) { Preference newPref = newPrefs.get(i); // If the newPref has a key and has a corresponding old preference, update the old // preference and give it a new order. Preference oldPref = newToOld.get(newPref); if (oldPref == null) { newPref.setOrder(i); screen.addPreference(newPref); continue; } oldPref.setOrder(i); if (oldPref instanceof EmbeddedSlicePreference) { // EmbeddedSlicePreference has its own slice observer // (EmbeddedSlicePreferenceHelper). Should therefore not be updated by // slice observer in SliceFragment. // The order will however still need to be updated, as this can not be handled // by EmbeddedSlicePreferenceHelper. continue; } oldPref.setTitle(newPref.getTitle()); oldPref.setSummary(newPref.getSummary()); oldPref.setEnabled(newPref.isEnabled()); oldPref.setSelectable(newPref.isSelectable()); oldPref.setFragment(newPref.getFragment()); oldPref.getExtras().putAll(newPref.getExtras()); if ((oldPref instanceof HasSliceAction) && (newPref instanceof HasSliceAction)) { ((HasSliceAction) oldPref) .setSliceAction( ((HasSliceAction) newPref).getSliceAction()); } if ((oldPref instanceof HasSliceUri) && (newPref instanceof HasSliceUri)) { ((HasSliceUri) oldPref) .setUri(((HasSliceUri) newPref).getUri()); } if ((oldPref instanceof HasCustomContentDescription) && (newPref instanceof HasCustomContentDescription)) { ((HasCustomContentDescription) oldPref).setContentDescription( ((HasCustomContentDescription) newPref) .getContentDescription()); } } //addPreference will reset the checked status of TwoStatePreference. //So we need to add them back for (int i = 0; i < screen.getPreferenceCount(); i++) { Preference screenPref = screen.getPreference(i); if (screenPref instanceof TwoStatePreference && twoStatePreferenceIsCheckedByOrder.get(screenPref.getOrder()) != null) { ((TwoStatePreference) screenPref) .setChecked(twoStatePreferenceIsCheckedByOrder.get(screenPref.getOrder())); } } } private static boolean isSamePreference(Preference oldPref, Preference newPref) { if (oldPref == null || newPref == null) { return false; } if (newPref instanceof HasSliceUri != oldPref instanceof HasSliceUri) { return false; } if (newPref instanceof EmbeddedSlicePreference) { return oldPref instanceof EmbeddedSlicePreference && Objects.equals(((EmbeddedSlicePreference) newPref).getUri(), ((EmbeddedSlicePreference) oldPref).getUri()); } else if (oldPref instanceof EmbeddedSlicePreference) { return false; } return newPref.getKey() != null && newPref.getKey().equals(oldPref.getKey()); } @Override public void onPreferenceFocused(Preference preference) { setLastFocused(preference); } @Override public void onSeekbarPreferenceChanged(SliceSeekbarPreference preference, int addValue) { if (DEBUG) Log.d(TAG, "onSeekbarPreferenceChanged, addValue: " + addValue); int curValue = preference.getValue(); onSeekbarPreferenceValueChanged(preference, curValue); } public void onSeekbarPreferenceValueChanged(SliceSeekbarPreference preference, int newValue) { if (DEBUG) Log.d(TAG, "onSeekbarPreferenceChanged, newValue: " + newValue); try { Intent fillInIntent = new Intent() .putExtra(EXTRA_PREFERENCE_KEY, preference.getKey()) .putExtra(SlicesConstants.SUBTYPE_SEEKBAR_VALUE, newValue); firePendingIntent(preference, fillInIntent); } catch (Exception e) { Log.e(TAG, "PendingIntent for slice cannot be sent", e); } } @Override public boolean onPreferenceTreeClick(Preference preference) { if (preference instanceof SliceRadioPreference) { SliceRadioPreference radioPref = (SliceRadioPreference) preference; if (!radioPref.isChecked()) { radioPref.setChecked(true); if (TextUtils.isEmpty(radioPref.getUri())) { return true; } } Intent fillInIntent = new Intent().putExtra(EXTRA_PREFERENCE_KEY, preference.getKey()); boolean result = firePendingIntent(radioPref, fillInIntent); radioPref.clearOtherRadioPreferences(getPreferenceScreen()); if (result) { return true; } } else if (preference instanceof TwoStatePreference && preference instanceof HasSliceAction) { boolean isChecked = ((TwoStatePreference) preference).isChecked(); preference.getExtras().putBoolean(EXTRA_PREFERENCE_INFO_STATUS, isChecked); Intent fillInIntent = new Intent() .putExtra(EXTRA_TOGGLE_STATE, isChecked) .putExtra(EXTRA_PREFERENCE_KEY, preference.getKey()); if (firePendingIntent((HasSliceAction) preference, fillInIntent)) { return true; } return true; } else if (preference instanceof SlicePreference) { Intent fillInIntent = new Intent().putExtra(EXTRA_PREFERENCE_KEY, preference.getKey()); if (firePendingIntent((HasSliceAction) preference, fillInIntent)) { return true; } } return super.onPreferenceTreeClick(preference); } private boolean firePendingIntent(@NonNull HasSliceAction preference, Intent fillInIntent) { if (preference.getSliceAction() == null) { return false; } IntentSender intentSender = preference.getSliceAction().getAction().getIntentSender(); mActivityResultLauncher.launch( new IntentSenderRequest.Builder(intentSender).setFillInIntent( fillInIntent).build()); if (preference.getFollowupSliceAction() != null) { mPreferenceFollowupIntent = preference.getFollowupSliceAction().getAction(); } return true; } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelable(KEY_PREFERENCE_FOLLOWUP_INTENT, mPreferenceFollowupIntent); outState.putInt(KEY_PREFERENCE_FOLLOWUP_RESULT_CODE, mFollowupPendingIntentResultCode); outState.putCharSequence(KEY_SCREEN_TITLE, mScreenTitle); outState.putCharSequence(KEY_SCREEN_SUBTITLE, mScreenSubtitle); outState.putString(KEY_LAST_PREFERENCE, mLastFocusedPreferenceKey); outState.putString(KEY_URI_STRING, mUriString); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (savedInstanceState != null) { mPreferenceFollowupIntent = savedInstanceState.getParcelable(KEY_PREFERENCE_FOLLOWUP_INTENT); mFollowupPendingIntentResultCode = savedInstanceState.getInt(KEY_PREFERENCE_FOLLOWUP_RESULT_CODE); mScreenTitle = savedInstanceState.getCharSequence(KEY_SCREEN_TITLE); mScreenSubtitle = savedInstanceState.getCharSequence(KEY_SCREEN_SUBTITLE); mLastFocusedPreferenceKey = savedInstanceState.getString(KEY_LAST_PREFERENCE); mUriString = savedInstanceState.getString(KEY_URI_STRING); } } @Override public void onChanged(Slice slice) { mSlice = slice; // Make TvSettings guard against the case that slice provider is not set up correctly if (slice == null || slice.getHints() == null) { return; } if (slice.getHints().contains(HINT_PARTIAL)) { showProgressBar(); } else { hideProgressBar(); } update(); } private void showProgressBar() { View view = this.getView(); View progressBar = view == null ? null : getView().findViewById(R.id.progress_bar); if (progressBar != null) { progressBar.bringToFront(); progressBar.setVisibility(View.VISIBLE); } } private void hideProgressBar() { View view = this.getView(); View progressBar = view == null ? null : getView().findViewById(R.id.progress_bar); if (progressBar != null) { progressBar.setVisibility(View.GONE); } } private void setSubtitle(CharSequence subtitle) { View view = this.getView(); if (view == null) { return; } TextView decorSubtitle = view.findViewById(R.id.decor_subtitle); if (decorSubtitle != null) { if (TextUtils.isEmpty(subtitle)) { decorSubtitle.setVisibility(View.GONE); } else { decorSubtitle.setVisibility(View.VISIBLE); decorSubtitle.setText(subtitle); } } mScreenSubtitle = subtitle; } @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final ViewGroup view = (ViewGroup) super.onCreateView(inflater, container, savedInstanceState); LayoutInflater themedInflater = LayoutInflater.from(getContext()); final View newTitleContainer = themedInflater.inflate( R.layout.media_output_settings_title, null); if (newTitleContainer != null) { newTitleContainer.setOutlineProvider(null); } view.removeView( view.findViewById(androidx.leanback.preference.R.id.decor_title_container)); view.addView(newTitleContainer, 0); view.setBackgroundResource(android.R.color.transparent); RecyclerView recyclerView = view.findViewById(androidx.leanback.preference.R.id.list); if (recyclerView != null) { recyclerView.addOnScrollListener( new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); Drawable foreground = FadingEdgeUtil.getForegroundDrawable( recyclerView, requireContext()); if (foreground != recyclerView.getForeground()) { recyclerView.setForeground(foreground); } } }); } final View newContainer = themedInflater.inflate(R.layout.media_output_settings_progress, null); if (newContainer != null) { ((ViewGroup) newContainer).addView(view); } return newContainer; } public void setLastFocused(Preference preference) { mLastFocusedPreferenceKey = preference.getKey(); } private void handleUri(Uri uri) { String uriString = uri.getQueryParameter(SlicesConstants.PARAMETER_URI); String errorMessage = uri.getQueryParameter(SlicesConstants.PARAMETER_ERROR); if (DEBUG) Log.d(TAG, "handleUri: " + uri); if (errorMessage != null) { Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_SHORT).show(); } // Provider should provide the correct slice uri in the parameter if it wants to do certain // action(includes go back, forward), otherwise TvSettings would ignore it. if (uriString == null || !uriString.equals(mUriString)) { return; } String direction = uri.getQueryParameter(SlicesConstants.PARAMETER_DIRECTION); if (DEBUG) Log.d(TAG, "direction: " + direction); if (direction != null) { if (direction.equals(SlicesConstants.BACKWARD)) { back(); } else if (direction.equals(SlicesConstants.EXIT)) { finish(); } } } private void finish() { if (getActivity() != null) { getActivity().setResult(Activity.RESULT_OK); getActivity().finish(); } } private int getPreferenceActionId(Preference preference) { if (preference instanceof HasSliceAction) { return ((HasSliceAction) preference).getActionId() != 0 ? ((HasSliceAction) preference).getActionId() : TvSettingsEnums.ENTRY_DEFAULT; } return TvSettingsEnums.ENTRY_DEFAULT; } @Override protected int getPageId() { return mCurrentPageId != 0 ? mCurrentPageId : TvSettingsEnums.PAGE_SLICE_DEFAULT; } @Deprecated public int getMetricsCategory() { return 0; } }