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