1 /* 2 * Copyright (C) 2021 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.settingslib.collapsingtoolbar; 18 19 import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL_FAST; 20 21 import android.app.ActionBar; 22 import android.app.Activity; 23 import android.content.res.Configuration; 24 import android.graphics.text.LineBreakConfig; 25 import android.os.Build; 26 import android.util.Log; 27 import android.view.LayoutInflater; 28 import android.view.View; 29 import android.view.ViewGroup; 30 import android.widget.FrameLayout; 31 import android.widget.Toolbar; 32 33 import androidx.annotation.NonNull; 34 import androidx.annotation.Nullable; 35 import androidx.appcompat.app.AppCompatActivity; 36 import androidx.coordinatorlayout.widget.CoordinatorLayout; 37 38 import com.android.settingslib.widget.R; 39 40 import com.google.android.material.appbar.AppBarLayout; 41 import com.google.android.material.appbar.CollapsingToolbarLayout; 42 43 /** 44 * A delegate that allows to use the collapsing toolbar layout in hosts that doesn't want/need to 45 * extend from {@link CollapsingToolbarBaseActivity} or from {@link CollapsingToolbarBaseFragment}. 46 */ 47 public class CollapsingToolbarDelegate { 48 private static final String TAG = "CTBdelegate"; 49 /** Interface to be implemented by the host of the Collapsing Toolbar. */ 50 public interface HostCallback { 51 /** 52 * Called when a Toolbar should be set on the host. 53 * 54 * <p>If the host wants action bar to be modified, it should return it. 55 */ 56 @Nullable setActionBar(Toolbar toolbar)57 ActionBar setActionBar(Toolbar toolbar); 58 59 /** Sets support tool bar and return support action bar, this is for AppCompatActivity. */ 60 @Nullable setActionBar( androidx.appcompat.widget.Toolbar toolbar)61 default androidx.appcompat.app.ActionBar setActionBar( 62 androidx.appcompat.widget.Toolbar toolbar) { 63 return null; 64 } 65 66 /** Sets a title on the host. */ setOuterTitle(CharSequence title)67 void setOuterTitle(CharSequence title); 68 } 69 70 private static final float TOOLBAR_LINE_SPACING_MULTIPLIER = 1.1f; 71 72 @Nullable 73 private CoordinatorLayout mCoordinatorLayout; 74 @Nullable 75 private CollapsingToolbarLayout mCollapsingToolbarLayout; 76 @Nullable 77 private AppBarLayout mAppBarLayout; 78 @NonNull 79 private Toolbar mToolbar; 80 @NonNull 81 private FrameLayout mContentFrameLayout; 82 @NonNull 83 private final HostCallback mHostCallback; 84 CollapsingToolbarDelegate(@onNull HostCallback hostCallback)85 public CollapsingToolbarDelegate(@NonNull HostCallback hostCallback) { 86 mHostCallback = hostCallback; 87 } 88 89 /** Method to call that creates the root view of the collapsing toolbar. */ 90 @SuppressWarnings("RestrictTo") onCreateView(@onNull LayoutInflater inflater, @Nullable ViewGroup container)91 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { 92 return onCreateView(inflater, container, null); 93 } 94 95 /** Method to call that creates the root view of the collapsing toolbar. */ 96 @SuppressWarnings("RestrictTo") onCreateView(@onNull LayoutInflater inflater, @Nullable ViewGroup container, Activity activity)97 View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, 98 Activity activity) { 99 final View view = 100 inflater.inflate(R.layout.collapsing_toolbar_base_layout, container, false); 101 if (view instanceof CoordinatorLayout) { 102 mCoordinatorLayout = (CoordinatorLayout) view; 103 } 104 mCollapsingToolbarLayout = view.findViewById(R.id.collapsing_toolbar); 105 mAppBarLayout = view.findViewById(R.id.app_bar); 106 if (mCollapsingToolbarLayout != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 107 mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER); 108 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 109 mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST); 110 mCollapsingToolbarLayout.setStaticLayoutBuilderConfigurer(builder -> 111 builder.setLineBreakConfig( 112 new LineBreakConfig.Builder() 113 .setLineBreakWordStyle( 114 LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) 115 .build())); 116 } 117 } 118 autoSetCollapsingToolbarLayoutScrolling(); 119 mContentFrameLayout = view.findViewById(R.id.content_frame); 120 if (activity instanceof AppCompatActivity) { 121 Log.d(TAG, "onCreateView: from AppCompatActivity and sub-class."); 122 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 123 initSupportActionBar(inflater); 124 } else { 125 initRSupportActionBar(view); 126 } 127 } else { 128 Log.d(TAG, "onCreateView: from NonAppCompatActivity."); 129 mToolbar = view.findViewById(R.id.action_bar); 130 final ActionBar actionBar = mHostCallback.setActionBar(mToolbar); 131 // Enable title and home button by default 132 if (actionBar != null) { 133 actionBar.setDisplayHomeAsUpEnabled(true); 134 actionBar.setHomeButtonEnabled(true); 135 actionBar.setDisplayShowTitleEnabled(true); 136 } 137 } 138 return view; 139 } 140 initSupportActionBar(@onNull LayoutInflater inflater)141 private void initSupportActionBar(@NonNull LayoutInflater inflater) { 142 if (mCollapsingToolbarLayout == null) { 143 return; 144 } 145 mCollapsingToolbarLayout.removeAllViews(); 146 inflater.inflate(R.layout.support_toolbar, mCollapsingToolbarLayout); 147 final androidx.appcompat.widget.Toolbar supportToolbar = 148 mCollapsingToolbarLayout.findViewById(R.id.support_action_bar); 149 final androidx.appcompat.app.ActionBar actionBar = 150 mHostCallback.setActionBar(supportToolbar); 151 if (actionBar != null) { 152 actionBar.setDisplayHomeAsUpEnabled(true); 153 actionBar.setHomeButtonEnabled(true); 154 actionBar.setDisplayShowTitleEnabled(true); 155 } 156 } 157 initRSupportActionBar(View view)158 private void initRSupportActionBar(View view) { 159 view.findViewById(R.id.action_bar).setVisibility(View.GONE); 160 final androidx.appcompat.widget.Toolbar supportToolbar = 161 view.findViewById(R.id.support_action_bar); 162 supportToolbar.setVisibility(View.VISIBLE); 163 final androidx.appcompat.app.ActionBar actionBar = 164 mHostCallback.setActionBar(supportToolbar); 165 if (actionBar != null) { 166 actionBar.setDisplayHomeAsUpEnabled(true); 167 actionBar.setHomeButtonEnabled(true); 168 actionBar.setDisplayShowTitleEnabled(true); 169 } 170 } 171 172 /** Return an instance of CoordinatorLayout. */ 173 @Nullable getCoordinatorLayout()174 public CoordinatorLayout getCoordinatorLayout() { 175 return mCoordinatorLayout; 176 } 177 178 /** Sets the title on the collapsing layout, delegating to host if needed. */ setTitle(CharSequence title)179 public void setTitle(CharSequence title) { 180 if (mCollapsingToolbarLayout != null) { 181 mCollapsingToolbarLayout.setTitle(title); 182 } else { 183 mHostCallback.setOuterTitle(title); 184 } 185 } 186 187 /** Returns an instance of collapsing toolbar. */ 188 @Nullable getCollapsingToolbarLayout()189 public CollapsingToolbarLayout getCollapsingToolbarLayout() { 190 return mCollapsingToolbarLayout; 191 } 192 193 /** Return the content frame layout. */ 194 @NonNull getContentFrameLayout()195 public FrameLayout getContentFrameLayout() { 196 return mContentFrameLayout; 197 } 198 getToolbar()199 public Toolbar getToolbar() { 200 return mToolbar; 201 } 202 203 /** Return an instance of app bar. */ 204 @Nullable getAppBarLayout()205 public AppBarLayout getAppBarLayout() { 206 return mAppBarLayout; 207 } 208 autoSetCollapsingToolbarLayoutScrolling()209 private void autoSetCollapsingToolbarLayoutScrolling() { 210 if (mAppBarLayout == null) { 211 return; 212 } 213 final CoordinatorLayout.LayoutParams params = 214 (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams(); 215 final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior(); 216 behavior.setDragCallback( 217 new AppBarLayout.Behavior.DragCallback() { 218 @Override 219 public boolean canDrag(@NonNull AppBarLayout appBarLayout) { 220 // Header can be scrolling while device in landscape mode and SDK > 33 221 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) { 222 return false; 223 } else { 224 return appBarLayout.getResources().getConfiguration().orientation 225 == Configuration.ORIENTATION_LANDSCAPE; 226 } 227 } 228 }); 229 params.setBehavior(behavior); 230 } 231 } 232