1 /* 2 * Copyright (C) 2014 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.tv.settings.dialog.old; 18 19 import android.app.Activity; 20 import android.app.Fragment; 21 import android.app.FragmentManager; 22 import android.app.FragmentManager.OnBackStackChangedListener; 23 import android.app.FragmentTransaction; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.graphics.Color; 27 import android.graphics.drawable.ColorDrawable; 28 import android.net.Uri; 29 import android.os.Build; 30 import android.os.Bundle; 31 import android.view.LayoutInflater; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.view.animation.Interpolator; 35 36 import com.android.tv.settings.R; 37 38 import java.util.ArrayList; 39 40 /** 41 * A DialogActivity has 2 fragments, a content fragment and a list fragment. 42 * <p> 43 * Subclasses should override to supply the content fragment and list items. 44 * <p> 45 * The DialogActivity will handle animating in and out. 46 * <p> 47 * This class will use a default layout, but a custom layout can be provided by 48 * calling {@link #setLayoutProperties} 49 */ 50 public abstract class DialogActivity extends Activity 51 implements ActionAdapter.Listener, OnBackStackChangedListener { 52 53 /** 54 * Dialog Content Fragment title. 55 */ 56 public static final String EXTRA_DIALOG_TITLE = "dialog_title"; 57 58 /** 59 * Dialog Content Fragment breadcrumb. 60 */ 61 public static final String EXTRA_DIALOG_BREADCRUMB = "dialog_breadcrumb"; 62 63 /** 64 * Dialog Content Fragment description. 65 */ 66 public static final String EXTRA_DIALOG_DESCRIPTION = "dialog_description"; 67 68 /** 69 * Dialog Content Fragment image uri. 70 */ 71 public static final String EXTRA_DIALOG_IMAGE_URI = "dialog_image_uri"; 72 73 /** 74 * Dialog Content Fragment image background color 75 */ 76 public static final String EXTRA_DIALOG_IMAGE_BACKGROUND_COLOR 77 = "dialog_image_background_color"; 78 79 /** 80 * Dialog Action Fragment actions starting index. 81 */ 82 public static final String EXTRA_DIALOG_ACTIONS_START_INDEX = "dialog_actions_start_index"; 83 84 /** 85 * Dialog Action Fragment actions. 86 */ 87 public static final String EXTRA_PARCELABLE_ACTIONS = "parcelable_actions"; 88 89 /** 90 * Whether DialogActivity should create Content Fragment and Action Fragment from extras. 91 */ 92 public static final String EXTRA_CREATE_FRAGMENT_FROM_EXTRA = "create_fragment_from_extra"; 93 94 public static final String TAG_DIALOG = "tag_dialog"; 95 public static final String BACKSTACK_NAME_DIALOG = "backstack_name_dialog"; 96 public static final String KEY_BACKSTACK_COUNT = "backstack_count"; 97 98 protected static final int ANIMATE_IN_DURATION = 250; 99 protected static final int ANIMATE_DELAY = 550; 100 protected static final int SLIDE_IN_STAGGER = 100; 101 protected static final int SLIDE_IN_DISTANCE = 120; 102 103 private DialogFragment mDialogFragment; 104 private int mLayoutResId = R.layout.two_pane_dialog_frame; 105 private View mContent; 106 private int mLastBackStackCount = 0; 107 108 public DialogActivity() { 109 mDialogFragment = new DialogFragment(); 110 mDialogFragment.setActivity(this); 111 } 112 113 public static Intent createIntent(Context context, String title, 114 String breadcrumb, String description, String imageUri, 115 ArrayList<Action> actions) { 116 return createIntent(context, title, breadcrumb, description, imageUri, 117 Color.TRANSPARENT, actions); 118 } 119 120 public static Intent createIntent(Context context, String title, 121 String breadcrumb, String description, String imageUri, 122 int imageBackground, ArrayList<Action> actions) { 123 Intent intent = new Intent(context, DialogActivity.class); 124 intent.putExtra(EXTRA_DIALOG_TITLE, title); 125 intent.putExtra(EXTRA_DIALOG_BREADCRUMB, breadcrumb); 126 intent.putExtra(EXTRA_DIALOG_DESCRIPTION, description); 127 intent.putExtra(EXTRA_DIALOG_IMAGE_URI, imageUri); 128 intent.putExtra(EXTRA_DIALOG_IMAGE_BACKGROUND_COLOR, imageBackground); 129 intent.putParcelableArrayListExtra(EXTRA_PARCELABLE_ACTIONS, actions); 130 131 return intent; 132 } 133 134 public static Intent createIntent(Context context, String title, 135 String breadcrumb, String description, String imageUri, 136 ArrayList<Action> actions, Class<? extends DialogActivity> activityClass) { 137 return createIntent(context, title, breadcrumb, description, imageUri, Color.TRANSPARENT, 138 actions, activityClass); 139 } 140 141 public static Intent createIntent(Context context, String title, 142 String breadcrumb, String description, String imageUri, int imageBackground, 143 ArrayList<Action> actions, Class<? extends DialogActivity> activityClass) { 144 Intent intent = new Intent(context, activityClass); 145 intent.putExtra(EXTRA_DIALOG_TITLE, title); 146 intent.putExtra(EXTRA_DIALOG_BREADCRUMB, breadcrumb); 147 intent.putExtra(EXTRA_DIALOG_DESCRIPTION, description); 148 intent.putExtra(EXTRA_DIALOG_IMAGE_URI, imageUri); 149 intent.putExtra(EXTRA_DIALOG_IMAGE_BACKGROUND_COLOR, imageBackground); 150 intent.putParcelableArrayListExtra(EXTRA_PARCELABLE_ACTIONS, actions); 151 152 return intent; 153 } 154 155 public static Intent createIntent(Context context, String title, 156 String breadcrumb, String description, String imageUri, int imageBackground, 157 ArrayList<Action> actions, Class<? extends DialogActivity> activityClass, 158 int startIndex) { 159 Intent intent = new Intent(context, activityClass); 160 intent.putExtra(EXTRA_DIALOG_TITLE, title); 161 intent.putExtra(EXTRA_DIALOG_BREADCRUMB, breadcrumb); 162 intent.putExtra(EXTRA_DIALOG_DESCRIPTION, description); 163 intent.putExtra(EXTRA_DIALOG_IMAGE_URI, imageUri); 164 intent.putExtra(EXTRA_DIALOG_IMAGE_BACKGROUND_COLOR, imageBackground); 165 intent.putParcelableArrayListExtra(EXTRA_PARCELABLE_ACTIONS, actions); 166 intent.putExtra(EXTRA_DIALOG_ACTIONS_START_INDEX, startIndex); 167 168 return intent; 169 } 170 171 public View getContentView() { 172 return mContent; 173 } 174 175 @Override 176 protected void onCreate(Bundle savedInstanceState) { 177 // TODO: replace these hardcoded values with the commented constants whenever Hangouts 178 // updates their manifest to build against JB MR2. 179 if (Build.VERSION.SDK_INT >= 18 /* Build.VERSION_CODES.JELLY_BEAN_MR2 */) { 180 getWindow().addFlags(0x02000000 181 /* WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN */); 182 } 183 if(savedInstanceState != null) { 184 mLastBackStackCount = savedInstanceState.getInt(KEY_BACKSTACK_COUNT); 185 } 186 187 super.onCreate(savedInstanceState); 188 getFragmentManager().addOnBackStackChangedListener(this); 189 190 LayoutInflater helium = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); 191 mContent = helium.inflate(mLayoutResId, null); 192 setContentView(mContent); 193 if (mLayoutResId == R.layout.two_pane_dialog_frame) { 194 helium.inflate(R.layout.dialog_container, (ViewGroup) mContent); 195 setDialogFragment(mDialogFragment); 196 } 197 198 Bundle bundle = getIntent().getExtras(); 199 if (bundle != null) { 200 boolean createFragmentFromExtra = bundle.getBoolean(EXTRA_CREATE_FRAGMENT_FROM_EXTRA); 201 if (createFragmentFromExtra) { 202 // If intent bundle is not null, and flag indicates that should create fragments, 203 // set ContentFragment and ActionFragment using bundle extras. 204 String title = bundle.getString(EXTRA_DIALOG_TITLE); 205 String breadcrumb = bundle.getString(EXTRA_DIALOG_BREADCRUMB); 206 String description = bundle.getString(EXTRA_DIALOG_DESCRIPTION); 207 String imageUriStr = bundle.getString(EXTRA_DIALOG_IMAGE_URI); 208 Uri imageUri = Uri.parse(imageUriStr); 209 int backgroundColor = bundle.getInt(EXTRA_DIALOG_IMAGE_BACKGROUND_COLOR); 210 211 ArrayList<Action> actions = 212 bundle.getParcelableArrayList(EXTRA_PARCELABLE_ACTIONS); 213 214 setContentFragment(ContentFragment.newInstance(title, breadcrumb, 215 description, imageUri, backgroundColor)); 216 217 setActionFragment(ActionFragment.newInstance(actions)); 218 } 219 } 220 } 221 222 @Override 223 protected void onSaveInstanceState(Bundle savedInstanceState) { 224 super.onSaveInstanceState(savedInstanceState); 225 savedInstanceState.putInt(KEY_BACKSTACK_COUNT, mLastBackStackCount); 226 } 227 228 @Override 229 protected void onStart() { 230 super.onStart(); 231 if (mLayoutResId == R.layout.two_pane_dialog_frame) { 232 getDialogFragment().performEntryTransition(); 233 } 234 } 235 236 @Override 237 public void onBackStackChanged() { 238 int count = getFragmentManager().getBackStackEntryCount(); 239 if (count > 0 && count < mLastBackStackCount && DialogActivity.BACKSTACK_NAME_DIALOG.equals( 240 getFragmentManager().getBackStackEntryAt(count - 1).getName())) { 241 getFragmentManager().popBackStack(); 242 } 243 mLastBackStackCount = count; 244 } 245 246 @Override 247 public void onActionClicked(Action action) { 248 Intent intent = action.getIntent(); 249 if (intent != null) { 250 startActivity(intent); 251 finish(); 252 } 253 } 254 255 /** 256 * Disables the entry animation that normally happens onStart(). 257 */ 258 protected void disableEntryAnimation() { 259 getDialogFragment().disableEntryAnimation(); 260 } 261 262 /** 263 * This method sets the layout property of this class. <br/> 264 * Activities extending {@link DialogActivity} should call this method 265 * before calling {@link #onCreate(Bundle)} if they want to have a 266 * custom view. 267 * 268 * @param layoutResId resource if of the activity layout 269 * @param contentAreaId id of the content area 270 * @param actionAreaId id of the action area 271 */ 272 protected void setLayoutProperties(int layoutResId, int contentAreaId, int actionAreaId) { 273 mLayoutResId = layoutResId; 274 getDialogFragment().setLayoutProperties(contentAreaId, actionAreaId); 275 } 276 277 /** 278 * Animates a view. 279 * 280 * @param v view to animate 281 * @param initAlpha initial alpha 282 * @param initTransX initial translation in the X 283 * @param delay delay in ms 284 * @param duration duration in ms 285 * @param interpolator interpolator to be used, can be null 286 * @param isIcon if {@code true}, this is the main icon being moved 287 */ 288 protected void prepareAndAnimateView(final View v, float initAlpha, float initTransX, int delay, 289 int duration, Interpolator interpolator, final boolean isIcon) { 290 getDialogFragment().prepareAndAnimateView( 291 v, initAlpha, initTransX, delay, duration, interpolator, isIcon); 292 } 293 294 /** 295 * Called when intro animation is finished. 296 * <p> 297 * If a subclass is going to alter the view, should wait until this is called. 298 */ 299 protected void onIntroAnimationFinished() { 300 getDialogFragment().onIntroAnimationFinished(); 301 } 302 303 protected boolean isIntroAnimationInProgress() { 304 return getDialogFragment().isIntroAnimationInProgress(); 305 } 306 307 protected ColorDrawable getBackgroundDrawable() { 308 return getDialogFragment().getBackgroundDrawable(); 309 } 310 311 protected void setBackgroundDrawable(ColorDrawable drawable) { 312 getDialogFragment().setBackgroundDrawable(drawable); 313 } 314 315 /** 316 * Sets the content fragment into the view. 317 */ 318 protected void setContentFragment(Fragment fragment) { 319 getDialogFragment().setContentFragment(fragment); 320 } 321 322 /** 323 * Sets the action fragment into the view. 324 * <p> 325 * If an action fragment currently exists, this will be added to the back stack. 326 */ 327 protected void setActionFragment(Fragment fragment) { 328 getDialogFragment().setActionFragment(fragment); 329 } 330 331 /** 332 * Sets the action fragment into the view. 333 * <p> 334 * If addToBackStack is true, and action fragment currently exists, 335 * this will be added to the back stack. 336 */ 337 protected void setActionFragment(Fragment fragment, boolean addToBackStack) { 338 getDialogFragment().setActionFragment(fragment, addToBackStack); 339 } 340 341 protected Fragment getActionFragment() { 342 return getDialogFragment().getActionFragment(); 343 } 344 345 protected Fragment getContentFragment() { 346 return getDialogFragment().getContentFragment(); 347 } 348 349 /** 350 * Set the content and action fragments in the same transaction. 351 * <p> 352 * If an action fragment currently exists, this will be added to the back stack. 353 */ 354 protected void setContentAndActionFragments(Fragment contentFragment, Fragment actionFragment) { 355 getDialogFragment().setContentAndActionFragments(contentFragment, actionFragment); 356 } 357 358 /** 359 * Set the content and action fragments in the same transaction. 360 * <p> 361 * If addToBackStack and an action fragment currently exists, 362 * this will be added to the back stack. 363 */ 364 protected void setContentAndActionFragments(Fragment contentFragment, Fragment actionFragment, 365 boolean addToBackStack) { 366 getDialogFragment().setContentAndActionFragments( 367 contentFragment, actionFragment, addToBackStack); 368 } 369 370 protected void setDialogFragment(DialogFragment fragment) { 371 setDialogFragment(fragment, true); 372 } 373 374 protected void setDialogFragment(DialogFragment fragment, boolean addToBackStack) { 375 mDialogFragment = fragment; 376 fragment.setActivity(this); 377 FragmentManager fm = getFragmentManager(); 378 FragmentTransaction ft = fm.beginTransaction(); 379 boolean hasDialog = fm.findFragmentByTag(DialogActivity.TAG_DIALOG) != null; 380 if (hasDialog) { 381 if (addToBackStack) { 382 ft.addToBackStack(DialogActivity.BACKSTACK_NAME_DIALOG); 383 } 384 } 385 ft.replace(R.id.dialog_fragment, fragment, DialogActivity.TAG_DIALOG); 386 ft.commit(); 387 } 388 389 protected DialogFragment getDialogFragment() { 390 FragmentManager fm = getFragmentManager(); 391 if (fm != null) { 392 DialogFragment fragment = (DialogFragment) fm.findFragmentByTag(TAG_DIALOG); 393 if (fragment != null) { 394 mDialogFragment = fragment; 395 } 396 } 397 398 return mDialogFragment; 399 } 400 } 401