Home | History | Annotate | Download | only in drawer
      1 /*
      2  * Copyright (C) 2017 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 androidx.car.drawer;
     18 
     19 import android.animation.ValueAnimator;
     20 import android.content.res.Configuration;
     21 import android.os.Bundle;
     22 import android.util.TypedValue;
     23 import android.view.LayoutInflater;
     24 import android.view.MenuItem;
     25 import android.view.View;
     26 import android.view.ViewGroup;
     27 
     28 import androidx.annotation.LayoutRes;
     29 import androidx.annotation.Nullable;
     30 import androidx.appcompat.app.ActionBarDrawerToggle;
     31 import androidx.appcompat.app.AppCompatActivity;
     32 import androidx.appcompat.widget.Toolbar;
     33 import androidx.car.R;
     34 import androidx.drawerlayout.widget.DrawerLayout;
     35 
     36 import com.google.android.material.appbar.AppBarLayout;
     37 
     38 /**
     39  * Common base Activity for car apps that need to present a Drawer.
     40  *
     41  * <p>This Activity manages the overall layout. To use it, sub-classes need to:
     42  *
     43  * <ul>
     44  *   <li>Provide the root-items for the drawer by calling {@link #getDrawerController()}.
     45  *       {@link CarDrawerController#setRootAdapter(CarDrawerAdapter)}.
     46  *   <li>Add their main content using {@link #setMainContent(int)} or {@link #setMainContent(View)}.
     47  *       They can also add fragments to the main-content container by obtaining its id using
     48  *       {@link #getContentContainerId()}
     49  * </ul>
     50  *
     51  * <p>This class will take care of drawer toggling and display.
     52  *
     53  * <p>This Activity also exposes the ability to have its toolbar optionally hide if any content
     54  * in its main view is scrolled. Be default, this ability is turned off. Call
     55  * {@link #setToolbarCollapsible()} to enable this behavior. Additionally, a user can set elevation
     56  * on this toolbar by calling the appropriate {@link #setToolbarElevation(float)} method. There is
     57  * elevation on the toolbar by default.
     58  *
     59  * <p>The rootAdapter can implement nested-navigation, in its click-handling, by passing the
     60  * CarDrawerAdapter for the next level to
     61  * {@link CarDrawerController#pushAdapter(CarDrawerAdapter)}.
     62  *
     63  * <p>Any Activity's based on this class need to set their theme to
     64  * {@code Theme.Car.Light.NoActionBar.Drawer} or a derivative.
     65  */
     66 public class CarDrawerActivity extends AppCompatActivity {
     67     private static final int ANIMATION_DURATION_MS = 100;
     68 
     69     private CarDrawerController mDrawerController;
     70     private AppBarLayout mAppBarLayout;
     71     private Toolbar mToolbar;
     72 
     73     @Override
     74     protected void onCreate(Bundle savedInstanceState) {
     75         super.onCreate(savedInstanceState);
     76 
     77         setContentView(R.layout.car_drawer_activity);
     78 
     79         mAppBarLayout = findViewById(R.id.appbar);
     80         mAppBarLayout.setBackgroundColor(getThemeColorPrimary());
     81         setToolbarElevation(getResources().getDimension(R.dimen.car_app_bar_default_elevation));
     82 
     83         DrawerLayout drawerLayout = findViewById(R.id.drawer_layout);
     84         ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(
     85                 /* activity= */ this,
     86                 drawerLayout,
     87                 R.string.car_drawer_open,
     88                 R.string.car_drawer_close);
     89 
     90         mToolbar = findViewById(R.id.car_toolbar);
     91         setSupportActionBar(mToolbar);
     92 
     93         mDrawerController = new CarDrawerController(drawerLayout, drawerToggle);
     94         CarDrawerAdapter rootAdapter = getRootAdapter();
     95         if (rootAdapter != null) {
     96             mDrawerController.setRootAdapter(rootAdapter);
     97         }
     98 
     99         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    100         getSupportActionBar().setHomeButtonEnabled(true);
    101     }
    102 
    103     /**
    104      * Returns the {@link CarDrawerController} that is responsible for handling events relating
    105      * to the drawer in this Activity.
    106      *
    107      * @return The {@link CarDrawerController} linked to this Activity. This value will be
    108      * {@code null} if this method is called before {@code onCreate()} has been called.
    109      */
    110     @Nullable
    111     protected CarDrawerController getDrawerController() {
    112         return mDrawerController;
    113     }
    114 
    115     @Override
    116     protected void onPostCreate(Bundle savedInstanceState) {
    117         super.onPostCreate(savedInstanceState);
    118         mDrawerController.syncState();
    119     }
    120 
    121     /**
    122      * @return Adapter for root content of the Drawer.
    123      * @deprecated Do not implement this, instead call {@link #getDrawerController}.
    124      * {@link CarDrawerController#setRootAdapter(CarDrawerAdapter)} directly.
    125      */
    126     @Deprecated
    127     @Nullable
    128     protected CarDrawerAdapter getRootAdapter() {
    129         return null;
    130     }
    131 
    132     /**
    133      * Set main content to display in this Activity. It will be added to R.id.content_frame in
    134      * car_drawer_activity.xml. NOTE: Do not use {@link #setContentView(View)}.
    135      *
    136      * @param view View to display as main content.
    137      */
    138     public void setMainContent(View view) {
    139         ViewGroup parent = findViewById(getContentContainerId());
    140         parent.addView(view);
    141     }
    142 
    143     /**
    144      * Set main content to display in this Activity. It will be added to R.id.content_frame in
    145      * car_drawer_activity.xml. NOTE: Do not use {@link #setContentView(int)}.
    146      *
    147      * @param resourceId Layout to display as main content.
    148      */
    149     public void setMainContent(@LayoutRes int resourceId) {
    150         ViewGroup parent = findViewById(getContentContainerId());
    151         LayoutInflater inflater = getLayoutInflater();
    152         inflater.inflate(resourceId, parent, true);
    153     }
    154 
    155     /**
    156      * Sets the elevation on the toolbar of this Activity.
    157      *
    158      * @param elevation The elevation to set.
    159      */
    160     public void setToolbarElevation(float elevation) {
    161         // The AppBar's default animator needs to be set to null to manually change the elevation.
    162         mAppBarLayout.setStateListAnimator(null);
    163         mAppBarLayout.setElevation(elevation);
    164     }
    165 
    166     /**
    167      * Sets the elevation of the toolbar and animate it from the current elevation value.
    168      *
    169      * @param elevation The elevation to set.
    170      */
    171     public void setToolbarElevationWithAnimation(float elevation) {
    172         ValueAnimator elevationAnimator =
    173                 ValueAnimator.ofFloat(mAppBarLayout.getElevation(), elevation);
    174         elevationAnimator
    175                 .setDuration(ANIMATION_DURATION_MS)
    176                 .addUpdateListener(animation -> setToolbarElevation(
    177                         (float) animation.getAnimatedValue()));
    178         elevationAnimator.start();
    179     }
    180 
    181     /**
    182      * Sets the toolbar of this Activity as collapsible. When any content in the main view of the
    183      * Activity is scrolled, the toolbar will collapse and show itself accordingly.
    184      */
    185     public void setToolbarCollapsible() {
    186         AppBarLayout.LayoutParams params =
    187                 (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
    188         params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
    189                 | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
    190     }
    191 
    192     /**
    193      * Sets the toolbar to always show even if content in the main view of the Activity has been
    194      * scrolled. This is the default behavior.
    195      */
    196     public void setToolbarAlwaysShow() {
    197         AppBarLayout.LayoutParams params =
    198                 (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
    199         params.setScrollFlags(0);
    200     }
    201 
    202     /**
    203      * Get the id of the main content Container which is a FrameLayout. Subclasses can add their own
    204      * content/fragments inside here.
    205      *
    206      * @return Id of FrameLayout where main content of the subclass Activity can be added.
    207      */
    208     protected int getContentContainerId() {
    209         return R.id.content_frame;
    210     }
    211 
    212     @Override
    213     protected void onStop() {
    214         super.onStop();
    215         mDrawerController.closeDrawer();
    216     }
    217 
    218     @Override
    219     public void onConfigurationChanged(Configuration newConfig) {
    220         super.onConfigurationChanged(newConfig);
    221         mDrawerController.onConfigurationChanged(newConfig);
    222     }
    223 
    224     @Override
    225     public boolean onOptionsItemSelected(MenuItem item) {
    226         return mDrawerController.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
    227     }
    228 
    229     /**
    230      * Returns the color that has been set as {@code colorPrimary} on the current Theme of this
    231      * Activity.
    232      */
    233     private int getThemeColorPrimary() {
    234         TypedValue value = new TypedValue();
    235         getTheme().resolveAttribute(android.R.attr.colorPrimary, value, true);
    236         return value.data;
    237     }
    238 }
    239