1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.developeroptions; 18 19 import android.app.Activity; 20 import android.app.Dialog; 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.Intent; 25 import android.content.pm.PackageManager; 26 import android.os.Bundle; 27 import android.text.TextUtils; 28 import android.util.ArrayMap; 29 import android.util.Log; 30 import android.view.LayoutInflater; 31 import android.view.View; 32 import android.view.ViewGroup; 33 import android.widget.Button; 34 35 import androidx.annotation.VisibleForTesting; 36 import androidx.annotation.XmlRes; 37 import androidx.fragment.app.DialogFragment; 38 import androidx.fragment.app.Fragment; 39 import androidx.preference.Preference; 40 import androidx.preference.PreferenceGroup; 41 import androidx.preference.PreferenceScreen; 42 import androidx.recyclerview.widget.LinearLayoutManager; 43 import androidx.recyclerview.widget.RecyclerView; 44 45 import com.android.car.developeroptions.core.InstrumentedPreferenceFragment; 46 import com.android.car.developeroptions.core.instrumentation.InstrumentedDialogFragment; 47 import com.android.car.developeroptions.search.actionbar.SearchMenuController; 48 import com.android.car.developeroptions.support.actionbar.HelpMenuController; 49 import com.android.car.developeroptions.support.actionbar.HelpResourceProvider; 50 import com.android.car.developeroptions.widget.HighlightablePreferenceGroupAdapter; 51 import com.android.car.developeroptions.widget.LoadingViewController; 52 import com.android.settingslib.CustomDialogPreferenceCompat; 53 import com.android.settingslib.CustomEditTextPreferenceCompat; 54 import com.android.settingslib.core.instrumentation.Instrumentable; 55 import com.android.settingslib.search.Indexable; 56 import com.android.settingslib.widget.LayoutPreference; 57 58 import java.util.UUID; 59 60 /** 61 * Base class for Settings fragments, with some helper functions and dialog management. 62 */ 63 public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceFragment 64 implements DialogCreatable, HelpResourceProvider, Indexable { 65 66 private static final String TAG = "SettingsPreference"; 67 68 private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted"; 69 70 71 private static final int ORDER_FIRST = -1; 72 73 private SettingsDialogFragment mDialogFragment; 74 // Cache the content resolver for async callbacks 75 private ContentResolver mContentResolver; 76 77 private RecyclerView.Adapter mCurrentRootAdapter; 78 private boolean mIsDataSetObserverRegistered = false; 79 private RecyclerView.AdapterDataObserver mDataSetObserver = 80 new RecyclerView.AdapterDataObserver() { 81 @Override 82 public void onChanged() { 83 onDataSetChanged(); 84 } 85 86 @Override 87 public void onItemRangeChanged(int positionStart, int itemCount) { 88 onDataSetChanged(); 89 } 90 91 @Override 92 public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { 93 onDataSetChanged(); 94 } 95 96 @Override 97 public void onItemRangeInserted(int positionStart, int itemCount) { 98 onDataSetChanged(); 99 } 100 101 @Override 102 public void onItemRangeRemoved(int positionStart, int itemCount) { 103 onDataSetChanged(); 104 } 105 106 @Override 107 public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { 108 onDataSetChanged(); 109 } 110 }; 111 112 private ViewGroup mPinnedHeaderFrameLayout; 113 private ViewGroup mButtonBar; 114 115 private LayoutPreference mHeader; 116 117 private View mEmptyView; 118 private LinearLayoutManager mLayoutManager; 119 private ArrayMap<String, Preference> mPreferenceCache; 120 private boolean mAnimationAllowed; 121 122 @VisibleForTesting 123 public HighlightablePreferenceGroupAdapter mAdapter; 124 @VisibleForTesting 125 public boolean mPreferenceHighlighted = false; 126 127 @Override onCreate(Bundle icicle)128 public void onCreate(Bundle icicle) { 129 super.onCreate(icicle); 130 SearchMenuController.init(this /* host */); 131 HelpMenuController.init(this /* host */); 132 133 if (icicle != null) { 134 mPreferenceHighlighted = icicle.getBoolean(SAVE_HIGHLIGHTED_KEY); 135 } 136 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(this /* host */); 137 } 138 139 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)140 public View onCreateView(LayoutInflater inflater, ViewGroup container, 141 Bundle savedInstanceState) { 142 final View root = super.onCreateView(inflater, container, savedInstanceState); 143 mPinnedHeaderFrameLayout = root.findViewById(R.id.pinned_header); 144 mButtonBar = root.findViewById(R.id.button_bar); 145 return root; 146 } 147 148 @Override addPreferencesFromResource(@mlRes int preferencesResId)149 public void addPreferencesFromResource(@XmlRes int preferencesResId) { 150 super.addPreferencesFromResource(preferencesResId); 151 checkAvailablePrefs(getPreferenceScreen()); 152 } 153 154 @VisibleForTesting checkAvailablePrefs(PreferenceGroup preferenceGroup)155 void checkAvailablePrefs(PreferenceGroup preferenceGroup) { 156 if (preferenceGroup == null) return; 157 for (int i = 0; i < preferenceGroup.getPreferenceCount(); i++) { 158 Preference pref = preferenceGroup.getPreference(i); 159 if (pref instanceof SelfAvailablePreference 160 && !((SelfAvailablePreference) pref).isAvailable(getContext())) { 161 pref.setVisible(false); 162 } else if (pref instanceof PreferenceGroup) { 163 checkAvailablePrefs((PreferenceGroup) pref); 164 } 165 } 166 } 167 getButtonBar()168 public ViewGroup getButtonBar() { 169 return mButtonBar; 170 } 171 setPinnedHeaderView(int layoutResId)172 public View setPinnedHeaderView(int layoutResId) { 173 final LayoutInflater inflater = getActivity().getLayoutInflater(); 174 final View pinnedHeader = 175 inflater.inflate(layoutResId, mPinnedHeaderFrameLayout, false); 176 setPinnedHeaderView(pinnedHeader); 177 return pinnedHeader; 178 } 179 setPinnedHeaderView(View pinnedHeader)180 public void setPinnedHeaderView(View pinnedHeader) { 181 mPinnedHeaderFrameLayout.addView(pinnedHeader); 182 mPinnedHeaderFrameLayout.setVisibility(View.VISIBLE); 183 } 184 185 @Override onSaveInstanceState(Bundle outState)186 public void onSaveInstanceState(Bundle outState) { 187 super.onSaveInstanceState(outState); 188 189 if (mAdapter != null) { 190 outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mAdapter.isHighlightRequested()); 191 } 192 } 193 194 @Override onActivityCreated(Bundle savedInstanceState)195 public void onActivityCreated(Bundle savedInstanceState) { 196 super.onActivityCreated(savedInstanceState); 197 setHasOptionsMenu(true); 198 } 199 200 @Override onResume()201 public void onResume() { 202 super.onResume(); 203 highlightPreferenceIfNeeded(); 204 } 205 206 @Override onBindPreferences()207 protected void onBindPreferences() { 208 registerObserverIfNeeded(); 209 } 210 211 @Override onUnbindPreferences()212 protected void onUnbindPreferences() { 213 unregisterObserverIfNeeded(); 214 } 215 setLoading(boolean loading, boolean animate)216 public void setLoading(boolean loading, boolean animate) { 217 View loadingContainer = getView().findViewById(R.id.loading_container); 218 LoadingViewController.handleLoadingContainer(loadingContainer, getListView(), 219 !loading /* done */, 220 animate); 221 } 222 registerObserverIfNeeded()223 public void registerObserverIfNeeded() { 224 if (!mIsDataSetObserverRegistered) { 225 if (mCurrentRootAdapter != null) { 226 mCurrentRootAdapter.unregisterAdapterDataObserver(mDataSetObserver); 227 } 228 mCurrentRootAdapter = getListView().getAdapter(); 229 mCurrentRootAdapter.registerAdapterDataObserver(mDataSetObserver); 230 mIsDataSetObserverRegistered = true; 231 onDataSetChanged(); 232 } 233 } 234 unregisterObserverIfNeeded()235 public void unregisterObserverIfNeeded() { 236 if (mIsDataSetObserverRegistered) { 237 if (mCurrentRootAdapter != null) { 238 mCurrentRootAdapter.unregisterAdapterDataObserver(mDataSetObserver); 239 mCurrentRootAdapter = null; 240 } 241 mIsDataSetObserverRegistered = false; 242 } 243 } 244 highlightPreferenceIfNeeded()245 public void highlightPreferenceIfNeeded() { 246 if (!isAdded()) { 247 return; 248 } 249 if (mAdapter != null) { 250 mAdapter.requestHighlight(getView(), getListView()); 251 } 252 } 253 254 /** 255 * Returns initial expanded child count. 256 * <p/> 257 * Only override this method if the initial expanded child must be determined at run time. 258 */ getInitialExpandedChildCount()259 public int getInitialExpandedChildCount() { 260 return 0; 261 } 262 onDataSetChanged()263 protected void onDataSetChanged() { 264 highlightPreferenceIfNeeded(); 265 updateEmptyView(); 266 } 267 getHeaderView()268 public LayoutPreference getHeaderView() { 269 return mHeader; 270 } 271 setHeaderView(int resource)272 protected void setHeaderView(int resource) { 273 mHeader = new LayoutPreference(getPrefContext(), resource); 274 addPreferenceToTop(mHeader); 275 } 276 setHeaderView(View view)277 protected void setHeaderView(View view) { 278 mHeader = new LayoutPreference(getPrefContext(), view); 279 addPreferenceToTop(mHeader); 280 } 281 addPreferenceToTop(LayoutPreference preference)282 private void addPreferenceToTop(LayoutPreference preference) { 283 preference.setOrder(ORDER_FIRST); 284 if (getPreferenceScreen() != null) { 285 getPreferenceScreen().addPreference(preference); 286 } 287 } 288 289 @Override setPreferenceScreen(PreferenceScreen preferenceScreen)290 public void setPreferenceScreen(PreferenceScreen preferenceScreen) { 291 if (preferenceScreen != null && !preferenceScreen.isAttached()) { 292 // Without ids generated, the RecyclerView won't animate changes to the preferences. 293 preferenceScreen.setShouldUseGeneratedIds(mAnimationAllowed); 294 } 295 super.setPreferenceScreen(preferenceScreen); 296 if (preferenceScreen != null) { 297 if (mHeader != null) { 298 preferenceScreen.addPreference(mHeader); 299 } 300 } 301 } 302 303 @VisibleForTesting updateEmptyView()304 void updateEmptyView() { 305 if (mEmptyView == null) return; 306 if (getPreferenceScreen() != null) { 307 final View listContainer = getActivity().findViewById(android.R.id.list_container); 308 boolean show = (getPreferenceScreen().getPreferenceCount() 309 - (mHeader != null ? 1 : 0)) <= 0 310 || (listContainer != null && listContainer.getVisibility() != View.VISIBLE); 311 mEmptyView.setVisibility(show ? View.VISIBLE : View.GONE); 312 } else { 313 mEmptyView.setVisibility(View.VISIBLE); 314 } 315 } 316 setEmptyView(View v)317 public void setEmptyView(View v) { 318 if (mEmptyView != null) { 319 mEmptyView.setVisibility(View.GONE); 320 } 321 mEmptyView = v; 322 updateEmptyView(); 323 } 324 getEmptyView()325 public View getEmptyView() { 326 return mEmptyView; 327 } 328 329 @Override onCreateLayoutManager()330 public RecyclerView.LayoutManager onCreateLayoutManager() { 331 mLayoutManager = new LinearLayoutManager(getContext()); 332 return mLayoutManager; 333 } 334 335 @Override onCreateAdapter(PreferenceScreen preferenceScreen)336 protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) { 337 final Bundle arguments = getArguments(); 338 mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen, 339 arguments == null 340 ? null : arguments.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY), 341 mPreferenceHighlighted); 342 return mAdapter; 343 } 344 setAnimationAllowed(boolean animationAllowed)345 protected void setAnimationAllowed(boolean animationAllowed) { 346 mAnimationAllowed = animationAllowed; 347 } 348 cacheRemoveAllPrefs(PreferenceGroup group)349 protected void cacheRemoveAllPrefs(PreferenceGroup group) { 350 mPreferenceCache = new ArrayMap<>(); 351 final int N = group.getPreferenceCount(); 352 for (int i = 0; i < N; i++) { 353 Preference p = group.getPreference(i); 354 if (TextUtils.isEmpty(p.getKey())) { 355 continue; 356 } 357 mPreferenceCache.put(p.getKey(), p); 358 } 359 } 360 getCachedPreference(String key)361 protected Preference getCachedPreference(String key) { 362 return mPreferenceCache != null ? mPreferenceCache.remove(key) : null; 363 } 364 removeCachedPrefs(PreferenceGroup group)365 protected void removeCachedPrefs(PreferenceGroup group) { 366 for (Preference p : mPreferenceCache.values()) { 367 group.removePreference(p); 368 } 369 mPreferenceCache = null; 370 } 371 getCachedCount()372 protected int getCachedCount() { 373 return mPreferenceCache != null ? mPreferenceCache.size() : 0; 374 } 375 376 @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) removePreference(String key)377 public boolean removePreference(String key) { 378 return removePreference(getPreferenceScreen(), key); 379 } 380 381 @VisibleForTesting removePreference(PreferenceGroup group, String key)382 boolean removePreference(PreferenceGroup group, String key) { 383 final int preferenceCount = group.getPreferenceCount(); 384 for (int i = 0; i < preferenceCount; i++) { 385 final Preference preference = group.getPreference(i); 386 final String curKey = preference.getKey(); 387 388 if (TextUtils.equals(curKey, key)) { 389 return group.removePreference(preference); 390 } 391 392 if (preference instanceof PreferenceGroup) { 393 if (removePreference((PreferenceGroup) preference, key)) { 394 return true; 395 } 396 } 397 } 398 return false; 399 } 400 401 /* 402 * The name is intentionally made different from Activity#finish(), so that 403 * users won't misunderstand its meaning. 404 */ finishFragment()405 public final void finishFragment() { 406 getActivity().onBackPressed(); 407 } 408 409 // Some helpers for functions used by the settings fragments when they were activities 410 411 /** 412 * Returns the ContentResolver from the owning Activity. 413 */ getContentResolver()414 protected ContentResolver getContentResolver() { 415 Context context = getActivity(); 416 if (context != null) { 417 mContentResolver = context.getContentResolver(); 418 } 419 return mContentResolver; 420 } 421 422 /** 423 * Returns the specified system service from the owning Activity. 424 */ getSystemService(final String name)425 protected Object getSystemService(final String name) { 426 return getActivity().getSystemService(name); 427 } 428 429 /** 430 * Returns the PackageManager from the owning Activity. 431 */ getPackageManager()432 protected PackageManager getPackageManager() { 433 return getActivity().getPackageManager(); 434 } 435 436 @Override onDetach()437 public void onDetach() { 438 if (isRemoving()) { 439 if (mDialogFragment != null) { 440 mDialogFragment.dismiss(); 441 mDialogFragment = null; 442 } 443 } 444 super.onDetach(); 445 } 446 447 // Dialog management 448 showDialog(int dialogId)449 protected void showDialog(int dialogId) { 450 if (mDialogFragment != null) { 451 Log.e(TAG, "Old dialog fragment not null!"); 452 } 453 mDialogFragment = SettingsDialogFragment.newInstance(this, dialogId); 454 mDialogFragment.show(getChildFragmentManager(), Integer.toString(dialogId)); 455 } 456 457 @Override onCreateDialog(int dialogId)458 public Dialog onCreateDialog(int dialogId) { 459 return null; 460 } 461 462 @Override getDialogMetricsCategory(int dialogId)463 public int getDialogMetricsCategory(int dialogId) { 464 return 0; 465 } 466 removeDialog(int dialogId)467 protected void removeDialog(int dialogId) { 468 // mDialogFragment may not be visible yet in parent fragment's onResume(). 469 // To be able to dismiss dialog at that time, don't check 470 // mDialogFragment.isVisible(). 471 if (mDialogFragment != null && mDialogFragment.getDialogId() == dialogId) { 472 mDialogFragment.dismissAllowingStateLoss(); 473 } 474 mDialogFragment = null; 475 } 476 477 /** 478 * Sets the OnCancelListener of the dialog shown. This method can only be 479 * called after showDialog(int) and before removeDialog(int). The method 480 * does nothing otherwise. 481 */ setOnCancelListener(DialogInterface.OnCancelListener listener)482 protected void setOnCancelListener(DialogInterface.OnCancelListener listener) { 483 if (mDialogFragment != null) { 484 mDialogFragment.mOnCancelListener = listener; 485 } 486 } 487 488 /** 489 * Sets the OnDismissListener of the dialog shown. This method can only be 490 * called after showDialog(int) and before removeDialog(int). The method 491 * does nothing otherwise. 492 */ setOnDismissListener(DialogInterface.OnDismissListener listener)493 protected void setOnDismissListener(DialogInterface.OnDismissListener listener) { 494 if (mDialogFragment != null) { 495 mDialogFragment.mOnDismissListener = listener; 496 } 497 } 498 onDialogShowing()499 public void onDialogShowing() { 500 // override in subclass to attach a dismiss listener, for instance 501 } 502 503 @Override onDisplayPreferenceDialog(Preference preference)504 public void onDisplayPreferenceDialog(Preference preference) { 505 if (preference.getKey() == null) { 506 // Auto-key preferences that don't have a key, so the dialog can find them. 507 preference.setKey(UUID.randomUUID().toString()); 508 } 509 DialogFragment f = null; 510 if (preference instanceof RestrictedListPreference) { 511 f = RestrictedListPreference.RestrictedListPreferenceDialogFragment 512 .newInstance(preference.getKey()); 513 } else if (preference instanceof CustomListPreference) { 514 f = CustomListPreference.CustomListPreferenceDialogFragment 515 .newInstance(preference.getKey()); 516 } else if (preference instanceof CustomDialogPreferenceCompat) { 517 f = CustomDialogPreferenceCompat.CustomPreferenceDialogFragment 518 .newInstance(preference.getKey()); 519 } else if (preference instanceof CustomEditTextPreferenceCompat) { 520 f = CustomEditTextPreferenceCompat.CustomPreferenceDialogFragment 521 .newInstance(preference.getKey()); 522 } else { 523 super.onDisplayPreferenceDialog(preference); 524 return; 525 } 526 f.setTargetFragment(this, 0); 527 f.show(getFragmentManager(), "dialog_preference"); 528 onDialogShowing(); 529 } 530 531 public static class SettingsDialogFragment extends InstrumentedDialogFragment { 532 private static final String KEY_DIALOG_ID = "key_dialog_id"; 533 private static final String KEY_PARENT_FRAGMENT_ID = "key_parent_fragment_id"; 534 535 private Fragment mParentFragment; 536 537 private DialogInterface.OnCancelListener mOnCancelListener; 538 private DialogInterface.OnDismissListener mOnDismissListener; 539 newInstance(DialogCreatable fragment, int dialogId)540 public static SettingsDialogFragment newInstance(DialogCreatable fragment, int dialogId) { 541 if (!(fragment instanceof Fragment)) { 542 throw new IllegalArgumentException("fragment argument must be an instance of " 543 + Fragment.class.getName()); 544 } 545 546 final SettingsDialogFragment settingsDialogFragment = new SettingsDialogFragment(); 547 settingsDialogFragment.setParentFragment(fragment); 548 settingsDialogFragment.setDialogId(dialogId); 549 550 return settingsDialogFragment; 551 } 552 553 @Override getMetricsCategory()554 public int getMetricsCategory() { 555 if (mParentFragment == null) { 556 return Instrumentable.METRICS_CATEGORY_UNKNOWN; 557 } 558 final int metricsCategory = 559 ((DialogCreatable) mParentFragment).getDialogMetricsCategory(mDialogId); 560 if (metricsCategory <= 0) { 561 throw new IllegalStateException("Dialog must provide a metrics category"); 562 } 563 return metricsCategory; 564 } 565 566 @Override onSaveInstanceState(Bundle outState)567 public void onSaveInstanceState(Bundle outState) { 568 super.onSaveInstanceState(outState); 569 if (mParentFragment != null) { 570 outState.putInt(KEY_DIALOG_ID, mDialogId); 571 outState.putInt(KEY_PARENT_FRAGMENT_ID, mParentFragment.getId()); 572 } 573 } 574 575 @Override onStart()576 public void onStart() { 577 super.onStart(); 578 579 if (mParentFragment != null && mParentFragment instanceof SettingsPreferenceFragment) { 580 ((SettingsPreferenceFragment) mParentFragment).onDialogShowing(); 581 } 582 } 583 584 @Override onCreateDialog(Bundle savedInstanceState)585 public Dialog onCreateDialog(Bundle savedInstanceState) { 586 if (savedInstanceState != null) { 587 mDialogId = savedInstanceState.getInt(KEY_DIALOG_ID, 0); 588 mParentFragment = getParentFragment(); 589 int mParentFragmentId = savedInstanceState.getInt(KEY_PARENT_FRAGMENT_ID, -1); 590 if (mParentFragment == null) { 591 mParentFragment = getFragmentManager().findFragmentById(mParentFragmentId); 592 } 593 if (!(mParentFragment instanceof DialogCreatable)) { 594 throw new IllegalArgumentException( 595 (mParentFragment != null 596 ? mParentFragment.getClass().getName() 597 : mParentFragmentId) 598 + " must implement " 599 + DialogCreatable.class.getName()); 600 } 601 // This dialog fragment could be created from non-SettingsPreferenceFragment 602 if (mParentFragment instanceof SettingsPreferenceFragment) { 603 // restore mDialogFragment in mParentFragment 604 ((SettingsPreferenceFragment) mParentFragment).mDialogFragment = this; 605 } 606 } 607 return ((DialogCreatable) mParentFragment).onCreateDialog(mDialogId); 608 } 609 610 @Override onCancel(DialogInterface dialog)611 public void onCancel(DialogInterface dialog) { 612 super.onCancel(dialog); 613 if (mOnCancelListener != null) { 614 mOnCancelListener.onCancel(dialog); 615 } 616 } 617 618 @Override onDismiss(DialogInterface dialog)619 public void onDismiss(DialogInterface dialog) { 620 super.onDismiss(dialog); 621 if (mOnDismissListener != null) { 622 mOnDismissListener.onDismiss(dialog); 623 } 624 } 625 getDialogId()626 public int getDialogId() { 627 return mDialogId; 628 } 629 630 @Override onDetach()631 public void onDetach() { 632 super.onDetach(); 633 634 // This dialog fragment could be created from non-SettingsPreferenceFragment 635 if (mParentFragment instanceof SettingsPreferenceFragment) { 636 // in case the dialog is not explicitly removed by removeDialog() 637 if (((SettingsPreferenceFragment) mParentFragment).mDialogFragment == this) { 638 ((SettingsPreferenceFragment) mParentFragment).mDialogFragment = null; 639 } 640 } 641 } 642 setParentFragment(DialogCreatable fragment)643 private void setParentFragment(DialogCreatable fragment) { 644 mParentFragment = (Fragment) fragment; 645 } 646 setDialogId(int dialogId)647 private void setDialogId(int dialogId) { 648 mDialogId = dialogId; 649 } 650 } 651 hasNextButton()652 protected boolean hasNextButton() { 653 return ((ButtonBarHandler) getActivity()).hasNextButton(); 654 } 655 getNextButton()656 protected Button getNextButton() { 657 return ((ButtonBarHandler) getActivity()).getNextButton(); 658 } 659 finish()660 public void finish() { 661 Activity activity = getActivity(); 662 if (activity == null) return; 663 if (getFragmentManager().getBackStackEntryCount() > 0) { 664 getFragmentManager().popBackStack(); 665 } else { 666 activity.finish(); 667 } 668 } 669 getIntent()670 protected Intent getIntent() { 671 if (getActivity() == null) { 672 return null; 673 } 674 return getActivity().getIntent(); 675 } 676 setResult(int result, Intent intent)677 protected void setResult(int result, Intent intent) { 678 if (getActivity() == null) { 679 return; 680 } 681 getActivity().setResult(result, intent); 682 } 683 setResult(int result)684 protected void setResult(int result) { 685 if (getActivity() == null) { 686 return; 687 } 688 getActivity().setResult(result); 689 } 690 } 691