• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.packageinstaller.permission.ui.wear;
18 
19 import android.app.Fragment;
20 import android.content.Context;
21 import android.os.Bundle;
22 import android.support.v7.widget.RecyclerView;
23 import android.support.wearable.view.WearableListView;
24 import android.text.Editable;
25 import android.text.TextWatcher;
26 import android.util.TypedValue;
27 import android.view.Gravity;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.view.WindowInsets;
31 import android.widget.LinearLayout;
32 import android.widget.TextView;
33 
34 import com.android.packageinstaller.permission.ui.wear.settings.ViewUtils;
35 import com.android.packageinstaller.R;
36 
37 /**
38  * Base settings Fragment that shows a title at the top of the page.
39  */
40 public abstract class TitledSettingsFragment extends Fragment implements
41         View.OnLayoutChangeListener, WearableListView.ClickListener {
42 
43     private static final int ITEM_CHANGE_DURATION_MS = 120;
44 
45     private static final String TAG = "TitledSettingsFragment";
46     private int mInitialHeaderHeight;
47 
48     protected TextView mHeader;
49     protected TextView mDetails;
50     protected WearableListView mWheel;
51 
52     private int mCharLimitShortTitle;
53     private int mCharLimitLine;
54     private int mChinOffset;
55 
56     private TextWatcher mHeaderTextWatcher = new TextWatcher() {
57         @Override
58         public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
59 
60         @Override
61         public void onTextChanged(CharSequence s, int start, int before, int count) {}
62 
63         @Override
64         public void afterTextChanged(Editable editable) {
65             adjustHeaderSize();
66         }
67     };
68 
adjustHeaderTranslation()69     private void adjustHeaderTranslation() {
70         int translation = 0;
71         if (mWheel.getChildCount() > 0) {
72             translation = mWheel.getCentralViewTop() - mWheel.getChildAt(0).getTop();
73         }
74 
75         float newTranslation = Math.min(Math.max(-mInitialHeaderHeight, -translation), 0);
76 
77         int position = mWheel.getChildAdapterPosition(mWheel.getChildAt(0));
78         if (position == 0 || newTranslation < 0) {
79             mHeader.setTranslationY(newTranslation);
80         }
81     }
82 
83     @Override
onTopEmptyRegionClick()84     public void onTopEmptyRegionClick() {
85     }
86 
87     @Override
onCreate(Bundle savedInstanceState)88     public void onCreate(Bundle savedInstanceState) {
89         super.onCreate(savedInstanceState);
90         mCharLimitShortTitle = getResources().getInteger(R.integer.short_title_length);
91         mCharLimitLine = getResources().getInteger(R.integer.char_limit_per_line);
92     }
93 
94     @Override
onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)95     public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft,
96                                int oldTop, int oldRight, int oldBottom) {
97         if (view == mHeader) {
98             mInitialHeaderHeight = bottom - top;
99             if (ViewUtils.getIsCircular(getContext())) {
100                 // We are adding more margin on circular screens, so we need to account for it and use
101                 // it for hiding the header.
102                 mInitialHeaderHeight +=
103                         ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin;
104             }
105         } else if (view == mWheel) {
106             adjustHeaderTranslation();
107         }
108     }
109 
initializeLayout(RecyclerView.Adapter adapter)110     protected void initializeLayout(RecyclerView.Adapter adapter) {
111         View v = getView();
112         mWheel = (WearableListView) v.findViewById(R.id.wheel);
113 
114         mHeader = (TextView) v.findViewById(R.id.header);
115         mHeader.addOnLayoutChangeListener(this);
116         mHeader.addTextChangedListener(mHeaderTextWatcher);
117 
118         mDetails = (TextView) v.findViewById(R.id.details);
119         mDetails.addOnLayoutChangeListener(this);
120 
121         mWheel.setAdapter(adapter);
122         mWheel.addOnScrollListener(new RecyclerView.OnScrollListener() {
123             @Override
124             public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
125             }
126 
127             @Override
128             public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
129                 adjustHeaderTranslation();
130             }
131         });
132         mWheel.setClickListener(this);
133         mWheel.addOnLayoutChangeListener(this);
134 
135         // Decrease item change animation duration to approximately half of the default duration.
136         RecyclerView.ItemAnimator itemAnimator = mWheel.getItemAnimator();
137         itemAnimator.setChangeDuration(ITEM_CHANGE_DURATION_MS);
138 
139         adjustHeaderSize();
140 
141         positionOnCircular(getContext(), mHeader, mDetails, mWheel);
142     }
143 
positionOnCircular(Context context, View header, View details, final ViewGroup wheel)144     public void positionOnCircular(Context context, View header, View details,
145             final ViewGroup wheel) {
146         if (ViewUtils.getIsCircular(context)) {
147             LinearLayout.LayoutParams params =
148                     (LinearLayout.LayoutParams) header.getLayoutParams();
149             params.topMargin = (int) context.getResources().getDimension(
150                     R.dimen.settings_header_top_margin_circular);
151             // Note that the margins are made symmetrical here. Since they're symmetrical we choose
152             // the smaller value to maximize usable width.
153             final int margin = (int) Math.min(context.getResources().getDimension(
154                     R.dimen.round_content_padding_left), context.getResources().getDimension(
155                     R.dimen.round_content_padding_right));
156             params.leftMargin = margin;
157             params.rightMargin = margin;
158             params.gravity = Gravity.CENTER_HORIZONTAL;
159             header.setLayoutParams(params);
160             details.setLayoutParams(params);
161 
162             if (header instanceof TextView) {
163                 ((TextView) header).setGravity(Gravity.CENTER);
164             }
165             if (details instanceof TextView) {
166                 ((TextView) details).setGravity(Gravity.CENTER);
167             }
168 
169             final int leftPadding = (int) context.getResources().getDimension(
170                     R.dimen.round_content_padding_left);
171             final int rightPadding = (int) context.getResources().getDimension(
172                     R.dimen.round_content_padding_right);
173             final int topPadding = (int) context.getResources().getDimension(
174                     R.dimen.settings_wearable_list_view_vertical_padding_round);
175             final int bottomPadding = (int) context.getResources().getDimension(
176                     R.dimen.settings_wearable_list_view_vertical_padding_round);
177             wheel.setPadding(leftPadding, topPadding, rightPadding, mChinOffset + bottomPadding);
178             wheel.setClipToPadding(false);
179 
180             wheel.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
181                 @Override
182                 public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
183                     mChinOffset = insets.getSystemWindowInsetBottom();
184                     wheel.setPadding(leftPadding, topPadding, rightPadding,
185                             mChinOffset + bottomPadding);
186                     // This listener is invoked after each time we navigate to SettingsActivity and
187                     // it keeps adding padding. We need to disable it after the first update.
188                     v.setOnApplyWindowInsetsListener(null);
189                     return insets.consumeSystemWindowInsets();
190                 }
191             });
192         } else {
193             int leftPadding = (int) context.getResources().getDimension(
194                     R.dimen.content_padding_left);
195             wheel.setPadding(leftPadding, wheel.getPaddingTop(), wheel.getPaddingRight(),
196                     wheel.getPaddingBottom());
197         }
198     }
199 
adjustHeaderSize()200     private void adjustHeaderSize() {
201         int length = mHeader.length();
202 
203         if (length <= mCharLimitShortTitle) {
204             mHeader.setTextSize(TypedValue.COMPLEX_UNIT_PX,
205                     getResources().getDimensionPixelSize(
206                             R.dimen.setting_short_header_text_size));
207         } else {
208             mHeader.setTextSize(TypedValue.COMPLEX_UNIT_PX,
209                     getResources().getDimensionPixelSize(
210                             R.dimen.setting_long_header_text_size));
211         }
212 
213         boolean singleLine = length <= mCharLimitLine;
214 
215         float height = getResources().getDimension(R.dimen.settings_header_base_height);
216         if (!singleLine) {
217             height += getResources().getDimension(R.dimen.setting_header_extra_line_height);
218         }
219         mHeader.setMinHeight((int) height);
220 
221         LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mHeader.getLayoutParams();
222         final Context context = getContext();
223         if (!singleLine) {
224             // Make the top margin a little bit smaller so there is more space for the title.
225             if (ViewUtils.getIsCircular(context)) {
226                 params.topMargin = getResources().getDimensionPixelSize(
227                         R.dimen.settings_header_top_margin_circular_multiline);
228             } else {
229                 params.topMargin = getResources().getDimensionPixelSize(
230                         R.dimen.settings_header_top_margin_multiline);
231             }
232         } else {
233             if (ViewUtils.getIsCircular(context)) {
234                 params.topMargin = getResources().getDimensionPixelSize(
235                         R.dimen.settings_header_top_margin_circular);
236             } else {
237                 params.topMargin = getResources().getDimensionPixelSize(
238                         R.dimen.settings_header_top_margin);
239             }
240         }
241         mHeader.setLayoutParams(params);
242     }
243 }
244