1a20818257e6927560a676d99dbc563a0394f74d8Jason Monk/* 2a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * Copyright (C) 2016 The Android Open Source Project 3a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * 4a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * except in compliance with the License. You may obtain a copy of the License at 6a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * 7a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * http://www.apache.org/licenses/LICENSE-2.0 8a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * 9a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * Unless required by applicable law or agreed to in writing, software distributed under the 10a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * KIND, either express or implied. See the License for the specific language governing 12a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * permissions and limitations under the License. 13a20818257e6927560a676d99dbc563a0394f74d8Jason Monk */ 14a20818257e6927560a676d99dbc563a0394f74d8Jason Monk 15a20818257e6927560a676d99dbc563a0394f74d8Jason Monkpackage com.android.systemui.statusbar.phone; 16a20818257e6927560a676d99dbc563a0394f74d8Jason Monk 17a20818257e6927560a676d99dbc563a0394f74d8Jason Monkimport android.annotation.Nullable; 18a20818257e6927560a676d99dbc563a0394f74d8Jason Monkimport android.content.Context; 19a20818257e6927560a676d99dbc563a0394f74d8Jason Monkimport android.util.AttributeSet; 20f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digmanimport android.view.Gravity; 21a20818257e6927560a676d99dbc563a0394f74d8Jason Monkimport android.view.View; 22a20818257e6927560a676d99dbc563a0394f74d8Jason Monkimport android.view.ViewGroup; 23a20818257e6927560a676d99dbc563a0394f74d8Jason Monkimport android.widget.LinearLayout; 24f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digmanimport android.widget.RelativeLayout; 25a20818257e6927560a676d99dbc563a0394f74d8Jason Monk 264da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monkimport java.util.ArrayList; 274da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk 28a20818257e6927560a676d99dbc563a0394f74d8Jason Monk/** 29a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * Automatically reverses the order of children as they are added. 30a20818257e6927560a676d99dbc563a0394f74d8Jason Monk * Also reverse the width and height values of layout params 31a20818257e6927560a676d99dbc563a0394f74d8Jason Monk */ 32a20818257e6927560a676d99dbc563a0394f74d8Jason Monkpublic class ReverseLinearLayout extends LinearLayout { 33a20818257e6927560a676d99dbc563a0394f74d8Jason Monk 34db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos /** If true, the layout is reversed vs. a regular linear layout */ 35db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos private boolean mIsLayoutReverse; 36db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos 37db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos /** If true, the layout is opposite to it's natural reversity from the layout direction */ 38db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos private boolean mIsAlternativeOrder; 394da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk 40a20818257e6927560a676d99dbc563a0394f74d8Jason Monk public ReverseLinearLayout(Context context, @Nullable AttributeSet attrs) { 41a20818257e6927560a676d99dbc563a0394f74d8Jason Monk super(context, attrs); 42a20818257e6927560a676d99dbc563a0394f74d8Jason Monk } 43a20818257e6927560a676d99dbc563a0394f74d8Jason Monk 44a20818257e6927560a676d99dbc563a0394f74d8Jason Monk @Override 454da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk protected void onFinishInflate() { 464da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk super.onFinishInflate(); 47db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos updateOrder(); 484da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk } 494da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk 504da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk @Override 51a20818257e6927560a676d99dbc563a0394f74d8Jason Monk public void addView(View child) { 52f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman reverseParams(child.getLayoutParams(), child, mIsLayoutReverse); 53db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos if (mIsLayoutReverse) { 54c44368016a192dfe381f5f73c07be42e705e64dbJorim Jaggi super.addView(child, 0); 55db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos } else { 56db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos super.addView(child); 574da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk } 58a20818257e6927560a676d99dbc563a0394f74d8Jason Monk } 59a20818257e6927560a676d99dbc563a0394f74d8Jason Monk 60a20818257e6927560a676d99dbc563a0394f74d8Jason Monk @Override 61a20818257e6927560a676d99dbc563a0394f74d8Jason Monk public void addView(View child, ViewGroup.LayoutParams params) { 62f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman reverseParams(params, child, mIsLayoutReverse); 63db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos if (mIsLayoutReverse) { 64c44368016a192dfe381f5f73c07be42e705e64dbJorim Jaggi super.addView(child, 0, params); 65db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos } else { 66db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos super.addView(child, params); 674da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk } 684da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk } 694da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk 704da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk @Override 71db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos public void onRtlPropertiesChanged(int layoutDirection) { 72db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos super.onRtlPropertiesChanged(layoutDirection); 73db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos updateOrder(); 74db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos } 75db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos 76db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos public void setAlternativeOrder(boolean alternative) { 77db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos mIsAlternativeOrder = alternative; 78db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos updateOrder(); 794da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk } 804da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk 814da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk /** 824da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk * In landscape, the LinearLayout is not auto mirrored since it is vertical. Therefore we 834da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk * have to do it manually 844da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk */ 85db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos private void updateOrder() { 86db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos boolean isLayoutRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; 87db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos boolean isLayoutReverse = isLayoutRtl ^ mIsAlternativeOrder; 88db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos 89db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos if (mIsLayoutReverse != isLayoutReverse) { 90db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos // reversity changed, swap the order of all views. 914da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk int childCount = getChildCount(); 924da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk ArrayList<View> childList = new ArrayList<>(childCount); 934da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk for (int i = 0; i < childCount; i++) { 944da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk childList.add(getChildAt(i)); 954da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk } 964da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk removeAllViews(); 974da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk for (int i = childCount - 1; i >= 0; i--) { 98f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman final View child = childList.get(i); 99f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman super.addView(child); 1004da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk } 101db12b15febb32b4d366b8469c49bd9e724ef40d6Adrian Roos mIsLayoutReverse = isLayoutReverse; 1024da1b4edf60f65dae3ae159c6d13e0eee3b64abaJason Monk } 103a20818257e6927560a676d99dbc563a0394f74d8Jason Monk } 104a20818257e6927560a676d99dbc563a0394f74d8Jason Monk 105f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman private static void reverseParams(ViewGroup.LayoutParams params, View child, 106f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman boolean isLayoutReverse) { 1073b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk if (child instanceof Reversable) { 108f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman ((Reversable) child).reverse(isLayoutReverse); 1093b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk } 1103b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk if (child.getPaddingLeft() == child.getPaddingRight() 1113b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk && child.getPaddingTop() == child.getPaddingBottom()) { 1123b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk child.setPadding(child.getPaddingTop(), child.getPaddingLeft(), 1133b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk child.getPaddingTop(), child.getPaddingLeft()); 1143b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk } 115a20818257e6927560a676d99dbc563a0394f74d8Jason Monk if (params == null) { 116a20818257e6927560a676d99dbc563a0394f74d8Jason Monk return; 117a20818257e6927560a676d99dbc563a0394f74d8Jason Monk } 118a20818257e6927560a676d99dbc563a0394f74d8Jason Monk int width = params.width; 119a20818257e6927560a676d99dbc563a0394f74d8Jason Monk params.width = params.height; 120a20818257e6927560a676d99dbc563a0394f74d8Jason Monk params.height = width; 121a20818257e6927560a676d99dbc563a0394f74d8Jason Monk } 122a20818257e6927560a676d99dbc563a0394f74d8Jason Monk 1233b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk public interface Reversable { 124f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman void reverse(boolean isLayoutReverse); 1253b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk } 1263b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk 127f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman public static class ReverseRelativeLayout extends RelativeLayout implements Reversable { 1283b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk 129f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman public ReverseRelativeLayout(Context context) { 1303b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk super(context); 1313b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk } 1323b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk 1333b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk @Override 134f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman public void reverse(boolean isLayoutReverse) { 135f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman updateGravity(isLayoutReverse); 136f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman reverseGroup(this, isLayoutReverse); 137f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman } 138f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman 139f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman private int mDefaultGravity = Gravity.NO_GRAVITY; 140f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman public void setDefaultGravity(int gravity) { 141f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman mDefaultGravity = gravity; 142f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman } 143f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman 144f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman public void updateGravity(boolean isLayoutReverse) { 145f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman // Flip gravity if top of bottom is used 146f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman if (mDefaultGravity != Gravity.TOP && mDefaultGravity != Gravity.BOTTOM) return; 147f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman 148f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman // Use the default (intended for 270 LTR and 90 RTL) unless layout is otherwise 149f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman int gravityToApply = mDefaultGravity; 150f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman if (isLayoutReverse) { 151f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman gravityToApply = mDefaultGravity == Gravity.TOP ? Gravity.BOTTOM : Gravity.TOP; 152f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman } 153f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman 154f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman if (getGravity() != gravityToApply) setGravity(gravityToApply); 155f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman } 156f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman } 157f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman 158f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman private static void reverseGroup(ViewGroup group, boolean isLayoutReverse) { 159f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman for (int i = 0; i < group.getChildCount(); i++) { 160f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman final View child = group.getChildAt(i); 161f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman reverseParams(child.getLayoutParams(), child, isLayoutReverse); 162f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman 163f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman // Recursively reverse all children 164f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman if (child instanceof ViewGroup) { 165f77fb9176e59419519ed09e6cbd9d053d5506f30Mike Digman reverseGroup((ViewGroup) child, isLayoutReverse); 1663b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk } 1673b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk } 1683b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk } 1693b3f1f521a6a7a097c996955d11c075a32fecea8Jason Monk 170a20818257e6927560a676d99dbc563a0394f74d8Jason Monk} 171