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.settings.common; 18 19 import android.car.drivingstate.CarUxRestrictions; 20 import android.car.drivingstate.CarUxRestrictionsManager.OnUxRestrictionsChangedListener; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentSender; 24 import android.os.Bundle; 25 import android.view.View; 26 import android.view.inputmethod.InputMethodManager; 27 import android.widget.Toast; 28 29 import androidx.annotation.Nullable; 30 import androidx.fragment.app.DialogFragment; 31 import androidx.fragment.app.Fragment; 32 import androidx.fragment.app.FragmentActivity; 33 import androidx.fragment.app.FragmentManager.OnBackStackChangedListener; 34 import androidx.preference.Preference; 35 import androidx.preference.PreferenceFragmentCompat; 36 37 import com.android.car.apps.common.util.Themes; 38 import com.android.car.settings.R; 39 40 /** 41 * Base activity class for car settings, provides a action bar with a back button that goes to 42 * previous activity. 43 */ 44 public abstract class BaseCarSettingsActivity extends FragmentActivity implements 45 FragmentController, OnUxRestrictionsChangedListener, UxRestrictionsProvider, 46 OnBackStackChangedListener, PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { 47 private static final Logger LOG = new Logger(BaseCarSettingsActivity.class); 48 49 private CarUxRestrictionsHelper mUxRestrictionsHelper; 50 private View mRestrictedMessage; 51 // Default to minimum restriction. 52 private CarUxRestrictions mCarUxRestrictions = new CarUxRestrictions.Builder( 53 /* reqOpt= */ true, 54 CarUxRestrictions.UX_RESTRICTIONS_BASELINE, 55 /* timestamp= */ 0 56 ).build(); 57 58 @Override onCreate(Bundle savedInstanceState)59 protected void onCreate(Bundle savedInstanceState) { 60 super.onCreate(savedInstanceState); 61 setContentView(R.layout.car_setting_activity); 62 if (mUxRestrictionsHelper == null) { 63 mUxRestrictionsHelper = new CarUxRestrictionsHelper(/* context= */ this, /* listener= */ 64 this); 65 } 66 mUxRestrictionsHelper.start(); 67 getSupportFragmentManager().addOnBackStackChangedListener(this); 68 mRestrictedMessage = findViewById(R.id.restricted_message); 69 70 launchIfDifferent(getInitialFragment()); 71 } 72 73 @Override onDestroy()74 public void onDestroy() { 75 super.onDestroy(); 76 mUxRestrictionsHelper.stop(); 77 mUxRestrictionsHelper = null; 78 } 79 80 @Override onBackPressed()81 public void onBackPressed() { 82 super.onBackPressed(); 83 hideKeyboard(); 84 // If the backstack is empty, finish the activity. 85 if (getSupportFragmentManager().getBackStackEntryCount() == 0) { 86 finish(); 87 } 88 } 89 90 @Override launchFragment(Fragment fragment)91 public void launchFragment(Fragment fragment) { 92 if (fragment instanceof DialogFragment) { 93 throw new IllegalArgumentException( 94 "cannot launch dialogs with launchFragment() - use showDialog() instead"); 95 } 96 97 getSupportFragmentManager() 98 .beginTransaction() 99 .setCustomAnimations( 100 Themes.getAttrResourceId(/* context= */ this, 101 android.R.attr.fragmentOpenEnterAnimation), 102 Themes.getAttrResourceId(/* context= */ this, 103 android.R.attr.fragmentOpenExitAnimation), 104 Themes.getAttrResourceId(/* context= */ this, 105 android.R.attr.fragmentCloseEnterAnimation), 106 Themes.getAttrResourceId(/* context= */ this, 107 android.R.attr.fragmentCloseExitAnimation)) 108 .replace(R.id.fragment_container, fragment, 109 Integer.toString(getSupportFragmentManager().getBackStackEntryCount())) 110 .addToBackStack(null) 111 .commit(); 112 } 113 114 @Override goBack()115 public void goBack() { 116 onBackPressed(); 117 } 118 119 @Override showBlockingMessage()120 public void showBlockingMessage() { 121 Toast.makeText(this, R.string.restricted_while_driving, Toast.LENGTH_SHORT).show(); 122 } 123 124 @Override showDialog(DialogFragment dialogFragment, @Nullable String tag)125 public void showDialog(DialogFragment dialogFragment, @Nullable String tag) { 126 dialogFragment.show(getSupportFragmentManager(), tag); 127 } 128 129 @Override 130 @Nullable findDialogByTag(String tag)131 public DialogFragment findDialogByTag(String tag) { 132 Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag); 133 if (fragment instanceof DialogFragment) { 134 return (DialogFragment) fragment; 135 } 136 return null; 137 } 138 139 @Override startActivityForResult(Intent intent, int requestCode, ActivityResultCallback callback)140 public void startActivityForResult(Intent intent, int requestCode, 141 ActivityResultCallback callback) { 142 throw new UnsupportedOperationException( 143 "Unimplemented for activities that implement FragmentController"); 144 } 145 146 @Override startIntentSenderForResult(IntentSender intent, int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues, Bundle options, ActivityResultCallback callback)147 public void startIntentSenderForResult(IntentSender intent, int requestCode, 148 @Nullable Intent fillInIntent, int flagsMask, int flagsValues, Bundle options, 149 ActivityResultCallback callback) { 150 throw new UnsupportedOperationException( 151 "Unimplemented for activities that implement FragmentController"); 152 } 153 154 @Override onUxRestrictionsChanged(CarUxRestrictions restrictionInfo)155 public void onUxRestrictionsChanged(CarUxRestrictions restrictionInfo) { 156 mCarUxRestrictions = restrictionInfo; 157 Fragment currentFragment = getCurrentFragment(); 158 if (currentFragment instanceof OnUxRestrictionsChangedListener) { 159 ((OnUxRestrictionsChangedListener) currentFragment) 160 .onUxRestrictionsChanged(restrictionInfo); 161 } 162 updateBlockingView(currentFragment); 163 } 164 165 @Override getCarUxRestrictions()166 public CarUxRestrictions getCarUxRestrictions() { 167 return mCarUxRestrictions; 168 } 169 170 @Override onBackStackChanged()171 public void onBackStackChanged() { 172 onUxRestrictionsChanged(getCarUxRestrictions()); 173 } 174 175 @Override onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref)176 public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) { 177 if (pref.getFragment() != null) { 178 Fragment fragment = Fragment.instantiate(/* context= */ this, pref.getFragment(), 179 pref.getExtras()); 180 launchFragment(fragment); 181 return true; 182 } 183 return false; 184 } 185 186 /** 187 * Gets the fragment to show onCreate. If null, the activity will not perform an initial 188 * fragment transaction. 189 */ 190 @Nullable getInitialFragment()191 protected abstract Fragment getInitialFragment(); 192 launchIfDifferent(Fragment newFragment)193 protected void launchIfDifferent(Fragment newFragment) { 194 Fragment currentFragment = getCurrentFragment(); 195 if ((newFragment != null) && differentFragment(newFragment, currentFragment)) { 196 LOG.d("launchIfDifferent: " + newFragment + " replacing " + currentFragment); 197 launchFragment(newFragment); 198 } 199 } 200 getCurrentFragment()201 protected Fragment getCurrentFragment() { 202 return getSupportFragmentManager().findFragmentById(R.id.fragment_container); 203 } 204 205 /** 206 * Returns {code true} if newFragment is different from current fragment. 207 */ differentFragment(Fragment newFragment, Fragment currentFragment)208 private boolean differentFragment(Fragment newFragment, Fragment currentFragment) { 209 return (currentFragment == null) 210 || (!currentFragment.getClass().equals(newFragment.getClass())); 211 } 212 hideKeyboard()213 private void hideKeyboard() { 214 InputMethodManager imm = (InputMethodManager) this.getSystemService( 215 Context.INPUT_METHOD_SERVICE); 216 imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0); 217 } 218 updateBlockingView(@ullable Fragment currentFragment)219 private void updateBlockingView(@Nullable Fragment currentFragment) { 220 if (currentFragment instanceof BaseFragment) { 221 boolean canBeShown = ((BaseFragment) currentFragment).canBeShown(mCarUxRestrictions); 222 mRestrictedMessage.setVisibility(canBeShown ? View.GONE : View.VISIBLE); 223 } 224 } 225 } 226