| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.packageinstaller.permission.ui.wear; |
| |
| import android.app.Fragment; |
| import android.content.Context; |
| import android.os.Bundle; |
| import android.support.v7.widget.RecyclerView; |
| import android.support.wearable.view.WearableListView; |
| import android.text.Editable; |
| import android.text.TextWatcher; |
| import android.util.TypedValue; |
| import android.view.Gravity; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.WindowInsets; |
| import android.widget.FrameLayout; |
| import android.widget.TextView; |
| |
| import com.android.packageinstaller.permission.ui.wear.settings.ViewUtils; |
| import com.android.packageinstaller.R; |
| |
| /** |
| * Base settings Fragment that shows a title at the top of the page. |
| */ |
| public abstract class TitledSettingsFragment extends Fragment implements |
| View.OnLayoutChangeListener, WearableListView.ClickListener { |
| |
| private static final int ITEM_CHANGE_DURATION_MS = 120; |
| |
| private static final String TAG = "TitledSettingsFragment"; |
| private int mInitialHeaderHeight; |
| |
| protected TextView mHeader; |
| protected WearableListView mWheel; |
| |
| private static boolean sInitialized; |
| private static int sCharLimitShortTitle; |
| private static int sCharLimitLine; |
| private static int mChinOffset; |
| |
| private TextWatcher mHeaderTextWatcher = new TextWatcher() { |
| @Override |
| public void beforeTextChanged(CharSequence s, int start, int count, int after) {} |
| |
| @Override |
| public void onTextChanged(CharSequence s, int start, int before, int count) {} |
| |
| @Override |
| public void afterTextChanged(Editable editable) { |
| adjustHeaderSize(); |
| } |
| |
| }; |
| |
| private void adjustHeaderTranslation() { |
| int translation = 0; |
| if (mWheel.getChildCount() > 0) { |
| translation = mWheel.getCentralViewTop() - mWheel.getChildAt(0).getTop(); |
| } |
| |
| float newTranslation = Math.min(Math.max(-mInitialHeaderHeight, -translation), 0); |
| |
| int position = mWheel.getChildAdapterPosition(mWheel.getChildAt(0)); |
| if (position == 0 || newTranslation < 0) { |
| mHeader.setTranslationY(newTranslation); |
| } |
| } |
| |
| @Override |
| public void onTopEmptyRegionClick() { |
| } |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| if (!sInitialized) { |
| sCharLimitShortTitle = getResources().getInteger(R.integer.short_title_length); |
| sCharLimitLine = getResources().getInteger(R.integer.char_limit_per_line); |
| sInitialized = true; |
| } |
| } |
| |
| @Override |
| public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, |
| int oldTop, int oldRight, int oldBottom) { |
| if (view == mHeader) { |
| mInitialHeaderHeight = bottom - top; |
| if (ViewUtils.getIsCircular(getContext())) { |
| // We are adding more margin on circular screens, so we need to account for it and use |
| // it for hiding the header. |
| mInitialHeaderHeight += |
| ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin; |
| } |
| } else if (view == mWheel) { |
| adjustHeaderTranslation(); |
| } |
| } |
| |
| protected void initializeLayout(RecyclerView.Adapter adapter) { |
| View v = getView(); |
| mWheel = (WearableListView) v.findViewById(R.id.wheel); |
| |
| mHeader = (TextView) v.findViewById(R.id.header); |
| mHeader.addOnLayoutChangeListener(this); |
| mHeader.addTextChangedListener(mHeaderTextWatcher); |
| |
| mWheel.setAdapter(adapter); |
| mWheel.addOnScrollListener(new RecyclerView.OnScrollListener() { |
| @Override |
| public void onScrollStateChanged(RecyclerView recyclerView, int newState) { |
| } |
| |
| @Override |
| public void onScrolled(RecyclerView recyclerView, int dx, int dy) { |
| adjustHeaderTranslation(); |
| } |
| }); |
| mWheel.setClickListener(this); |
| mWheel.addOnLayoutChangeListener(this); |
| |
| // Decrease item change animation duration to approximately half of the default duration. |
| RecyclerView.ItemAnimator itemAnimator = mWheel.getItemAnimator(); |
| itemAnimator.setChangeDuration(ITEM_CHANGE_DURATION_MS); |
| |
| adjustHeaderSize(); |
| |
| positionOnCircular(getContext(), mHeader, mWheel); |
| } |
| |
| public static void positionOnCircular(Context context, View header, final ViewGroup wheel) { |
| if (ViewUtils.getIsCircular(context)) { |
| FrameLayout.LayoutParams params = |
| (FrameLayout.LayoutParams) header.getLayoutParams(); |
| params.topMargin = (int) context.getResources().getDimension( |
| R.dimen.settings_header_top_margin_circular); |
| // Note that the margins are made symmetrical here. Since they're symmetrical we choose |
| // the smaller value to maximize usable width. |
| final int margin = (int) Math.min(context.getResources().getDimension( |
| R.dimen.round_content_padding_left), context.getResources().getDimension( |
| R.dimen.round_content_padding_right)); |
| params.leftMargin = margin; |
| params.rightMargin = margin; |
| params.gravity = Gravity.CENTER_HORIZONTAL; |
| header.setLayoutParams(params); |
| |
| if (header instanceof TextView) { |
| ((TextView) header).setGravity(Gravity.CENTER); |
| } |
| |
| final int leftPadding = (int) context.getResources().getDimension( |
| R.dimen.round_content_padding_left); |
| final int rightPadding = (int) context.getResources().getDimension( |
| R.dimen.round_content_padding_right); |
| final int topPadding = (int) context.getResources().getDimension( |
| R.dimen.settings_wearable_list_view_vertical_padding_round); |
| final int bottomPadding = (int) context.getResources().getDimension( |
| R.dimen.settings_wearable_list_view_vertical_padding_round); |
| wheel.setPadding(leftPadding, topPadding, rightPadding, mChinOffset + bottomPadding); |
| wheel.setClipToPadding(false); |
| |
| wheel.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { |
| @Override |
| public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { |
| mChinOffset = insets.getSystemWindowInsetBottom(); |
| wheel.setPadding(leftPadding, topPadding, rightPadding, |
| mChinOffset + bottomPadding); |
| // This listener is invoked after each time we navigate to SettingsActivity and |
| // it keeps adding padding. We need to disable it after the first update. |
| v.setOnApplyWindowInsetsListener(null); |
| return insets.consumeSystemWindowInsets(); |
| } |
| }); |
| } else { |
| int leftPadding = (int) context.getResources().getDimension( |
| R.dimen.content_padding_left); |
| wheel.setPadding(leftPadding, wheel.getPaddingTop(), wheel.getPaddingRight(), |
| wheel.getPaddingBottom()); |
| } |
| } |
| |
| private void adjustHeaderSize() { |
| int length = mHeader.length(); |
| |
| if (length <= sCharLimitShortTitle) { |
| mHeader.setTextSize(TypedValue.COMPLEX_UNIT_PX, |
| getResources().getDimensionPixelSize( |
| R.dimen.setting_short_header_text_size)); |
| } else { |
| mHeader.setTextSize(TypedValue.COMPLEX_UNIT_PX, |
| getResources().getDimensionPixelSize( |
| R.dimen.setting_long_header_text_size)); |
| } |
| |
| boolean singleLine = length <= sCharLimitLine; |
| |
| float height = getResources().getDimension(R.dimen.settings_header_base_height); |
| if (!singleLine) { |
| height += getResources().getDimension(R.dimen.setting_header_extra_line_height); |
| } |
| |
| FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mHeader.getLayoutParams(); |
| params.height = (int) height; |
| final Context context = getContext(); |
| if (!singleLine) { |
| // Make the top margin a little bit smaller so there is more space for the title. |
| if (ViewUtils.getIsCircular(context)) { |
| params.topMargin = getResources().getDimensionPixelSize( |
| R.dimen.settings_header_top_margin_circular_multiline); |
| } else { |
| params.topMargin = getResources().getDimensionPixelSize( |
| R.dimen.settings_header_top_margin_multiline); |
| } |
| } else { |
| if (ViewUtils.getIsCircular(context)) { |
| params.topMargin = getResources().getDimensionPixelSize( |
| R.dimen.settings_header_top_margin_circular); |
| } else { |
| params.topMargin = getResources().getDimensionPixelSize( |
| R.dimen.settings_header_top_margin); |
| } |
| } |
| mHeader.setLayoutParams(params); |
| } |
| |
| |
| } |