|
| 1 | +// Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +package org.chromium.chrome.browser.firstrun; |
| 6 | + |
| 7 | +import android.content.Context; |
| 8 | +import android.content.res.Resources; |
| 9 | +import android.support.annotation.AnyRes; |
| 10 | +import android.util.DisplayMetrics; |
| 11 | +import android.util.TypedValue; |
| 12 | +import android.view.Gravity; |
| 13 | +import android.view.View; |
| 14 | +import android.view.ViewGroup; |
| 15 | +import android.widget.FrameLayout; |
| 16 | + |
| 17 | +import org.chromium.chrome.R; |
| 18 | + |
| 19 | +/** |
| 20 | + * FirstRunActivity variant that fills the whole screen, but displays the content |
| 21 | + * in a dialog-like layout. |
| 22 | + */ |
| 23 | +public class TabbedModeFirstRunActivity extends FirstRunActivity { |
| 24 | + @Override |
| 25 | + protected View createContentView() { |
| 26 | + return wrapInDialogLayout(super.createContentView()); |
| 27 | + } |
| 28 | + |
| 29 | + /** |
| 30 | + * Wraps contentView into layout that resembles DialogWhenLarge. The layout centers |
| 31 | + * the content and dims the background to simulate a modal dialog. |
| 32 | + */ |
| 33 | + private View wrapInDialogLayout(View contentView) { |
| 34 | + ContentLayout contentLayout = new ContentLayout(this); |
| 35 | + contentLayout.addView(contentView); |
| 36 | + |
| 37 | + contentLayout.setBackgroundResource(R.drawable.bg_white_dialog); |
| 38 | + |
| 39 | + // We need an outer layout for two things: |
| 40 | + // * centering the content |
| 41 | + // * dimming the background |
| 42 | + FrameLayout outerLayout = new FrameLayout(this); |
| 43 | + outerLayout.addView(contentLayout, |
| 44 | + new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, |
| 45 | + ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); |
| 46 | + outerLayout.setBackgroundResource(R.color.modal_dialog_scrim_color); |
| 47 | + return outerLayout; |
| 48 | + } |
| 49 | + |
| 50 | + /** |
| 51 | + * Layout that sizes itself according to DialogWhenLarge constraints. |
| 52 | + */ |
| 53 | + private static class ContentLayout extends FrameLayout { |
| 54 | + private TypedValue mFixedWidthMajor = new TypedValue(); |
| 55 | + private TypedValue mFixedWidthMinor = new TypedValue(); |
| 56 | + private TypedValue mFixedHeightMajor = new TypedValue(); |
| 57 | + private TypedValue mFixedHeightMinor = new TypedValue(); |
| 58 | + |
| 59 | + public ContentLayout(Context context) { |
| 60 | + super(context); |
| 61 | + fetchConstraints(); |
| 62 | + } |
| 63 | + |
| 64 | + /** |
| 65 | + * Wrapper around Resources.getValue() that translates Resources.NotFoundException |
| 66 | + * into false return value. Otherwise the function returns true. |
| 67 | + */ |
| 68 | + private boolean safeGetResourceValue(@AnyRes int id, TypedValue value) { |
| 69 | + try { |
| 70 | + getContext().getResources().getValue(id, value, true); |
| 71 | + return true; |
| 72 | + } catch (Resources.NotFoundException e) { |
| 73 | + return false; |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + private void fetchConstraints() { |
| 78 | + // Fetch size constraints. These are copies of corresponding abc_* AppCompat values, |
| 79 | + // because abc_* values are private, and even though corresponding theme attributes |
| 80 | + // are public in AppCompat (e.g. windowFixedWidthMinor), there is no guarantee that |
| 81 | + // AppCompat.DialogWhenLarge is actually defined by AppCompat and not based on |
| 82 | + // system DialogWhenLarge theme. |
| 83 | + // Note that we don't care about the return values, because onMeasure() handles null |
| 84 | + // constraints (and they will be null when the device is not considered "large"). |
| 85 | + safeGetResourceValue(R.dimen.dialog_fixed_width_minor, mFixedWidthMinor); |
| 86 | + safeGetResourceValue(R.dimen.dialog_fixed_width_major, mFixedWidthMajor); |
| 87 | + safeGetResourceValue(R.dimen.dialog_fixed_height_minor, mFixedHeightMinor); |
| 88 | + safeGetResourceValue(R.dimen.dialog_fixed_height_major, mFixedHeightMajor); |
| 89 | + } |
| 90 | + |
| 91 | + @Override |
| 92 | + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| 93 | + final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); |
| 94 | + final boolean isPortrait = metrics.widthPixels < metrics.heightPixels; |
| 95 | + |
| 96 | + // Constraint handling is adapted from |
| 97 | + // sdk/sources/android-25/android/support/v7/widget/ContentFrameLayout.java. |
| 98 | + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); |
| 99 | + assert widthMode == MeasureSpec.AT_MOST; |
| 100 | + { |
| 101 | + final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor; |
| 102 | + int widthSize = MeasureSpec.getSize(widthMeasureSpec); |
| 103 | + if (tvw.type != TypedValue.TYPE_NULL) { |
| 104 | + assert tvw.type == TypedValue.TYPE_FRACTION; |
| 105 | + int width = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels); |
| 106 | + widthSize = Math.min(width, widthSize); |
| 107 | + } |
| 108 | + // This either sets the width calculated from the constraints, or simply |
| 109 | + // takes all of the available space (as if MATCH_PARENT was specified). |
| 110 | + // The behavior is similar to how DialogWhenLarge windows are sized - they |
| 111 | + // are either sized by the constraints, or are full screen, but are never |
| 112 | + // sized based on the content size. |
| 113 | + widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY); |
| 114 | + } |
| 115 | + |
| 116 | + // This is similar to the above block that calculates width. |
| 117 | + final int heightMode = MeasureSpec.getMode(heightMeasureSpec); |
| 118 | + assert heightMode == MeasureSpec.AT_MOST; |
| 119 | + { |
| 120 | + final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor; |
| 121 | + int heightSize = MeasureSpec.getSize(heightMeasureSpec); |
| 122 | + if (tvh.type != TypedValue.TYPE_NULL) { |
| 123 | + assert tvh.type == TypedValue.TYPE_FRACTION; |
| 124 | + int height = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels); |
| 125 | + heightSize = Math.min(height, heightSize); |
| 126 | + } |
| 127 | + heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY); |
| 128 | + } |
| 129 | + |
| 130 | + super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
| 131 | + } |
| 132 | + } |
| 133 | +} |
0 commit comments