blob: a738decd1ddc4b0e83744c10c66163e5c0e83f14 [file] [log] [blame]
/*
* 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);
}
}