diff --git a/.gitignore b/.gitignore index 40302dc..5c42b3c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,10 +20,6 @@ gen-external-apklibs #gradle .gradle -local.properties -gradlew -gradlew.bat -gradle/ build/ #vi diff --git a/README.md b/README.md index e69de29..10ea820 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,42 @@ +### Working with shared elements + +Commit - [**8872619**](https://github.com/saulmm/Android-Material-Examples/commit/8872619ba751d99044a8426a35e6d2416d3af286) + +![](https://lh4.googleusercontent.com/-dmDFoF7c5UY/VD77NsFK8kI/AAAAAAAAuLM/MtSqP8JoQco/w282-h499-no/2014-10-16%2B00_51_34.gif) + +### Working with shared elements, explode transition, and animations + +Commit - [**901d62c**](https://github.com/saulmm/Android-Material-Examples/commit/901d62c65bbe70f55d944b718b8e6c6c2c69839d) + +![](https://lh6.googleusercontent.com/-ecoWQejLmFE/VEBoRo3Qs_I/AAAAAAAAuTU/YZ7855GmOfw/w276-h498-no/appear.gif) + +## Playing with the new menu-to arrow toggle animattion added in the Support Library + +Commit - [**ead8ada**](https://github.com/saulmm/Android-Material-Examples/commit/ead8adaafb482bd29a9ef86568a08afebeffd043) + +![](https://0b53cc8370cd7638a6bca2ead36937afe31044d3.googledrive.com/host/0B62SZ3WRM2R2aVA4TDVRV04walU/gif-arrow.gif) + +## Reusing an example to build a pretty effect like the tab slide of google play app + +Commit - [**f502305**](https://github.com/saulmm/Android-Material-Examples/commit/f5023057d1e088cc69849c9c7f60d32279604214) + +![](https://lh5.googleusercontent.com/-N-0aREntDdM/VEvP4I5kxrI/AAAAAAAAvQA/3CRH8pH5Vq4/s1006-no/sliding.gif) + +## Using CircularReveal to show a pretty effect between activities + +Commit - [**ba9c202**](https://github.com/saulmm/Android-Material-Examples/commit/ba9c2026bbe5fc57d6166389f60030176d065623) + +![](https://lh3.googleusercontent.com/-eQoRYLME7-0/VFEZ9qNaR7I/AAAAAAAAvqg/xk6U4UT_FUY/w420-h747-no/reveal.gif) + +## Replicating some parts of the lollypop dialer app basing on the material design principle: Motion provides meaning + +Commit - [**eb20707**](https://github.com/saulmm/Android-Material-Examples/commit/eb20707d8db3aef321ae30cb80faada5e3cd7b4d) + +![](https://lh6.googleusercontent.com/-t40vK2mcvkI/VF4-e7AzhBI/AAAAAAAAwnQ/lyzfRIj9heo/w800-h1422-no/dialer.gif) + + +## Using StateListAnimator to change the elevation of a view smootly + +Commit - [**3f1102b**](https://github.com/saulmm/Android-Material-Examples/commit/3f1102b50606d2b57781ad5c0de232697da47862) + +![](https://lh4.googleusercontent.com/-qqERGSF7t7Q/VHdlcZmbPgI/AAAAAAAAx1Q/CPo4dJk2_MM/w364-h644-no/elevation_sample.gif) diff --git a/app/build.gradle b/app/build.gradle index 0d34007..924e79c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,25 +1,22 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 'android-L' + compileSdkVersion 22 buildToolsVersion "20.0.0" defaultConfig { applicationId "com.saulmm.material.myapplication" - minSdkVersion 'L' - targetSdkVersion 'L' + minSdkVersion 21 + targetSdkVersion 22 versionCode 1 versionName "1.0" } - buildTypes { - release { - runProguard false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } + } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:support-v4:20.0.0' -} + compile 'com.android.support:cardview-v7:21.0.0' + compile 'com.android.support:appcompat-v7:22.0.0' + compile 'com.android.support:support-v4:22.0.0' +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/saulmm/material/myapplication/ApplicationTest.java b/app/src/androidTest/java/com/saulmm/material/myapplication/ApplicationTest.java deleted file mode 100644 index 2475efc..0000000 --- a/app/src/androidTest/java/com/saulmm/material/myapplication/ApplicationTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.saulmm.material.myapplication; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - super(Application.class); - } -} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a096cfd..2ebe7f2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,18 +7,35 @@ android:icon="@drawable/ic_launcher" android:label="" android:theme="@style/AppTheme" > - + + + - + + + + + + + + + android:name="com.saulmm.material.activities.ColorActivity" + android:label="@string/title_activity_color" /> + + + diff --git a/app/src/main/java/com/saulmm/material/MyActivity.java b/app/src/main/java/com/saulmm/material/MyActivity.java deleted file mode 100644 index 65c57af..0000000 --- a/app/src/main/java/com/saulmm/material/MyActivity.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.saulmm.material; - -import android.app.Activity; -import android.app.ActivityOptions; -import android.content.Intent; -import android.graphics.Outline; -import android.os.Bundle; -import android.transition.Explode; -import android.transition.Scene; -import android.transition.Slide; -import android.transition.TransitionManager; -import android.util.Pair; -import android.view.Menu; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.Toast; - -import com.saulmm.material.R; - - -public class MyActivity extends Activity { - - private FrameLayout frameContainer; - private View holderView; - private View fabButton; - - @Override - protected void onCreate(Bundle savedInstanceState) { - - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_my); - - // Fab Button - int fabSize = getResources().getDimensionPixelSize(R.dimen.fab_size); - Outline fabOutLine = new Outline(); - fabOutLine.setOval(0, 0, fabSize, fabSize); - - // Fragment container - frameContainer = (FrameLayout) findViewById(R.id.container); - - holderView = findViewById(R.id.holder_view); - - // FabView - fabButton = findViewById(R.id.fab_button); - fabButton.setOnClickListener(fabClickListener); - fabButton.setOutline(fabOutLine); - } - - - View.OnClickListener fabClickListener = new View.OnClickListener() { - @Override - public void onClick(View view) { - ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(MyActivity.this, - Pair.create(holderView, "holder2"), - Pair.create(fabButton, "fab")); - - Intent i = new Intent (MyActivity.this, MyActivity2.class); - startActivity(i, options.toBundle()); - } - }; -} diff --git a/app/src/main/java/com/saulmm/material/MyActivity2.java b/app/src/main/java/com/saulmm/material/MyActivity2.java deleted file mode 100644 index 282fb98..0000000 --- a/app/src/main/java/com/saulmm/material/MyActivity2.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.saulmm.material; - -import android.app.Activity; -import android.app.Fragment; -import android.graphics.Outline; -import android.os.Bundle; -import android.transition.ChangeBounds; -import android.transition.Explode; -import android.transition.Scene; -import android.transition.Slide; -import android.transition.TransitionManager; -import android.view.Menu; -import android.view.MenuItem; -import android.widget.FrameLayout; - -import com.saulmm.material.R; - -public class MyActivity2 extends Activity { - - private FrameLayout frameContainer; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_my2); - - // Fab Button - int fabSize = getResources().getDimensionPixelSize(R.dimen.fab_size); - Outline fabOutLine = new Outline(); - fabOutLine.setOval(0, 0, fabSize, fabSize); - } - - @Override - protected void onResume() { - - super.onResume(); - - frameContainer = (FrameLayout) findViewById(R.id.container); - - Scene scene1 = Scene.getSceneForLayout(frameContainer, R.layout.activity_my_activity2, this); - TransitionManager.go(scene1, new Explode()); - } -} diff --git a/app/src/main/java/com/saulmm/material/activities/ColorActivity.java b/app/src/main/java/com/saulmm/material/activities/ColorActivity.java new file mode 100644 index 0000000..f7a1ec4 --- /dev/null +++ b/app/src/main/java/com/saulmm/material/activities/ColorActivity.java @@ -0,0 +1,162 @@ +package com.saulmm.material.activities; + +import android.animation.Animator; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.TypedValue; +import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.Switch; + +import com.saulmm.material.R; +import com.saulmm.material.utils.GUIUtils; + +public class ColorActivity extends Activity { + + private SharedPreferences sharedpreferences; + private View revealView; + + @Override + protected void onCreate(final Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + + // Set the saved theme + sharedpreferences = getSharedPreferences("test", Context.MODE_PRIVATE); + setTheme(sharedpreferences.getInt("theme", R.style.AppTheme)); + + setContentView(R.layout.activity_color); + + // Views + Switch themeSwitch = (Switch) findViewById(R.id.theme_switch); + themeSwitch.setChecked(sharedpreferences.getBoolean(themeSwitch.getId()+"", false)); + themeSwitch.setOnCheckedChangeListener(checkedListener); + + CheckBox themeCheck = (CheckBox) findViewById(R.id.theme_check); + themeCheck.setChecked(sharedpreferences.getBoolean(themeCheck.getId()+"", false)); + themeCheck.setOnCheckedChangeListener(checkedListener); + + revealView = findViewById(R.id.reveal_view); + + // Show the unReveal effect + final int cx = sharedpreferences.getInt("x", 0); + final int cy = sharedpreferences.getInt("y", 0); + + startHideRevealEffect(cx, cy); + } + + private void startHideRevealEffect(final int cx, final int cy) { + + if (cx != 0 && cy != 0) { + // Show the unReveal effect when the view is attached to the window + revealView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + + // Get the accent color + TypedValue outValue = new TypedValue(); + getTheme().resolveAttribute(android.R.attr.colorPrimary, outValue, true); + revealView.setBackgroundColor(outValue.data); + + GUIUtils.hideRevealEffect(revealView, cx, cy, 1920); + } + + @Override + public void onViewDetachedFromWindow(View v) {} + }); + } + } + + + private void hideNavigationStatus() { + + View decorView = getWindow().getDecorView(); + + int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + decorView.setSystemUiVisibility(uiOptions); + } + + + Animator.AnimatorListener revealAnimationListener = new Animator.AnimatorListener() { + + @Override + public void onAnimationStart(Animator animation) {} + + @Override + public void onAnimationEnd(Animator animation) { + + Intent i = new Intent(ColorActivity.this, ColorActivity.class); + i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + startActivity(i); + overridePendingTransition(0, 0); + finish(); + } + + @Override + public void onAnimationCancel(Animator animation) {} + + @Override + public void onAnimationRepeat(Animator animation) {} + }; + + + public void view(View view) { + + int selectedTheme = 0; + int primaryColor = 0; + + switch (view.getId()) { + case R.id.circle1: + selectedTheme = R.style.theme1; + primaryColor = getResources().getColor(R.color.color_set_1_primary); + break; + + case R.id.circle2: + selectedTheme = R.style.theme2; + primaryColor = getResources().getColor(R.color.color_set_2_primary); + break; + + case R.id.circle3: + selectedTheme = R.style.theme3; + primaryColor = getResources().getColor(R.color.color_set_3_primary); + break; + + case R.id.circle4: + selectedTheme = R.style.theme4; + primaryColor = getResources().getColor(R.color.color_set_4_primary); + break; + } + + int [] location = new int[2]; + revealView.setBackgroundColor(primaryColor); + view.getLocationOnScreen(location); + + int cx = (location[0] + (view.getWidth() / 2)); + int cy = location[1] + (GUIUtils.getStatusBarHeight(this) / 2); + + SharedPreferences.Editor ed = sharedpreferences.edit(); + ed.putInt("x", cx); + ed.putInt("y", cy); + ed.putInt("theme", selectedTheme); + ed.apply(); + + hideNavigationStatus(); + GUIUtils.showRevealEffect(revealView, cx, cy, revealAnimationListener); + } + + + CompoundButton.OnCheckedChangeListener checkedListener = new CompoundButton.OnCheckedChangeListener() { + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + SharedPreferences.Editor ed = sharedpreferences.edit(); + ed.putInt("theme", (isChecked) ? R.style.Base_Theme_AppCompat : R.style.Base_Theme_AppCompat_Light); + ed.putBoolean(buttonView.getId()+"", isChecked); + ed.apply(); + } + }; +} diff --git a/app/src/main/java/com/saulmm/material/activities/DialerSampleActivity.java b/app/src/main/java/com/saulmm/material/activities/DialerSampleActivity.java new file mode 100644 index 0000000..527a101 --- /dev/null +++ b/app/src/main/java/com/saulmm/material/activities/DialerSampleActivity.java @@ -0,0 +1,47 @@ + + +package com.saulmm.material.activities; + +import android.graphics.Point; +import android.os.Bundle; +import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.Toolbar; +import android.view.Display; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.ArrayAdapter; +import android.widget.FrameLayout; +import android.widget.GridView; +import android.widget.ImageButton; + +import com.saulmm.material.R; +import com.saulmm.material.utils.GUIUtils; +import com.saulmm.material.views.adapters.SamplePagerAdapter; +import com.saulmm.material.views.widgets.SlidingTabLayout; + +public class DialerSampleActivity extends ActionBarActivity { + + private int screenWidth; + private ImageButton fabButton; + private FrameLayout dialerKeysContainer; + private float absolutefabPosition; + + @Override + protected void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_dialer); + + configureToolbar(); + } + + private void configureToolbar() { + + Toolbar mainToolbar = (Toolbar) findViewById(R.id.activity_dialer_toolbar); + mainToolbar.setTitleTextColor(getResources().getColor(R.color.theme_dialer_accent)); + setSupportActionBar(mainToolbar); + getSupportActionBar().setTitle("Dialer"); + } +} diff --git a/app/src/main/java/com/saulmm/material/activities/ElevationSampleActivity.java b/app/src/main/java/com/saulmm/material/activities/ElevationSampleActivity.java new file mode 100644 index 0000000..a7ea4db --- /dev/null +++ b/app/src/main/java/com/saulmm/material/activities/ElevationSampleActivity.java @@ -0,0 +1,17 @@ +package com.saulmm.material.activities; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + +import com.saulmm.material.R; + +public class ElevationSampleActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_elevation_sample); + } +} diff --git a/app/src/main/java/com/saulmm/material/activities/SlidingActivity.java b/app/src/main/java/com/saulmm/material/activities/SlidingActivity.java new file mode 100644 index 0000000..98f92c5 --- /dev/null +++ b/app/src/main/java/com/saulmm/material/activities/SlidingActivity.java @@ -0,0 +1,114 @@ +/* +* Copyright 2013 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.saulmm.material.activities; + +import android.app.FragmentTransaction; +import android.content.res.Configuration; +import android.os.Bundle; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.widget.Toolbar; +import android.view.Gravity; +import android.view.MenuItem; +import android.view.View; + +import com.saulmm.material.R; +import com.saulmm.material.fragments.SlidingTabFragment; + +public class SlidingActivity extends ActionBarActivity { + + private DrawerLayout mDrawerLayout; + private ActionBarDrawerToggle mDrawerToggle; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_sliding); + + if (savedInstanceState == null) { + FragmentTransaction transaction = getFragmentManager().beginTransaction(); + SlidingTabFragment fragment = new SlidingTabFragment(); + transaction.replace(R.id.sample_content_fragment, fragment); + transaction.commit(); + } + + configureToolbar(); + configureDrawer(); + } + + private void configureToolbar() { + Toolbar mainToolbar = (Toolbar) findViewById(R.id.activity_transition_header); + setSupportActionBar(mainToolbar); + getSupportActionBar().setTitle("Sliding"); + + mainToolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + if (mDrawerLayout.isDrawerOpen(Gravity.START)) { + mDrawerLayout.closeDrawer(Gravity.START); + + } else { + mDrawerLayout.openDrawer(Gravity.START); + } + } + }); + } + + private void configureDrawer() { + // Configure drawer + mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + + mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, + R.string.drawer_open, + R.string.drawer_closed) { + + public void onDrawerClosed(View view) { + supportInvalidateOptionsMenu(); + } + + public void onDrawerOpened(View drawerView) { + supportInvalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() + } + }; + + mDrawerLayout.setDrawerListener(mDrawerToggle); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + if (mDrawerToggle.onOptionsItemSelected(item)) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + mDrawerToggle.syncState(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mDrawerToggle.onConfigurationChanged(newConfig); + } +} diff --git a/app/src/main/java/com/saulmm/material/activities/TransitionFirstActivity.java b/app/src/main/java/com/saulmm/material/activities/TransitionFirstActivity.java new file mode 100644 index 0000000..23e359b --- /dev/null +++ b/app/src/main/java/com/saulmm/material/activities/TransitionFirstActivity.java @@ -0,0 +1,45 @@ +package com.saulmm.material.activities; + +import android.app.Activity; +import android.app.ActivityOptions; +import android.content.Intent; +import android.os.Bundle; +import android.transition.Slide; +import android.util.Pair; +import android.view.Gravity; +import android.view.View; + +import com.saulmm.material.R; + +public class TransitionFirstActivity extends Activity { + + private View mFabButton; + private View mHeader; + + @Override + protected void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_transition_first); + + mFabButton = findViewById(R.id.fab_button); + mHeader = findViewById(R.id.activity_transition_header); + + Slide slideExitTransition = new Slide(Gravity.BOTTOM); + slideExitTransition.excludeTarget(android.R.id.navigationBarBackground, true); + slideExitTransition.excludeTarget(android.R.id.statusBarBackground, true); + slideExitTransition.excludeTarget(R.id.activity_transition_header, true); + getWindow().setExitTransition(slideExitTransition); + } + + public void onFabPressed(View view) { + + Intent i = new Intent (TransitionFirstActivity.this, + TransitionSecondActivity.class); + + ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation( + TransitionFirstActivity.this,Pair.create(mFabButton, "fab"), Pair.create(mHeader, "holder1")); + + startActivity(i, transitionActivityOptions.toBundle()); + } +} diff --git a/app/src/main/java/com/saulmm/material/activities/TransitionSecondActivity.java b/app/src/main/java/com/saulmm/material/activities/TransitionSecondActivity.java new file mode 100644 index 0000000..376c26c --- /dev/null +++ b/app/src/main/java/com/saulmm/material/activities/TransitionSecondActivity.java @@ -0,0 +1,75 @@ +package com.saulmm.material.activities; + +import android.animation.Animator; +import android.app.Activity; +import android.os.Bundle; +import android.transition.Slide; +import android.transition.Transition; +import android.view.Gravity; +import android.view.View; +import android.view.ViewPropertyAnimator; +import android.widget.LinearLayout; + +import com.saulmm.material.R; +import com.saulmm.material.utils.AnimatorAdapter; +import com.saulmm.material.utils.TransitionAdapter; + +public class TransitionSecondActivity extends Activity { + + private static final int SCALE_DELAY = 30; + private LinearLayout rowContainer; + + @Override + protected void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_transition_second); + + rowContainer = (LinearLayout) findViewById(R.id.row_container2); + + Slide slideExitTransition = new Slide(Gravity.BOTTOM); + slideExitTransition.excludeTarget(android.R.id.navigationBarBackground, true); + slideExitTransition.excludeTarget(android.R.id.statusBarBackground, true); + + + getWindow().getEnterTransition().addListener(new TransitionAdapter() { + + @Override + public void onTransitionEnd(Transition transition) { + + super.onTransitionEnd(transition); + + getWindow().getEnterTransition().removeListener(this); + + for (int i = 0; i < rowContainer.getChildCount(); i++) { + + View rowView = rowContainer.getChildAt(i); + rowView.animate().setStartDelay(i * SCALE_DELAY) + .scaleX(1).scaleY(1); + } + } + }); + } + + @Override + public void onBackPressed() { + + for (int i = 0; i < rowContainer.getChildCount(); i++) { + + View rowView = rowContainer.getChildAt(i); + + ViewPropertyAnimator propertyAnimator = rowView.animate() + .setStartDelay(i * SCALE_DELAY) + .scaleX(0).scaleY(0) + .setListener(new AnimatorAdapter() { + + @Override + public void onAnimationEnd(Animator animation) { + + super.onAnimationEnd(animation); + finishAfterTransition(); + } + }); + } + } +} diff --git a/app/src/main/java/com/saulmm/material/fragments/NavigationFragment.java b/app/src/main/java/com/saulmm/material/fragments/NavigationFragment.java new file mode 100644 index 0000000..2738c3f --- /dev/null +++ b/app/src/main/java/com/saulmm/material/fragments/NavigationFragment.java @@ -0,0 +1,19 @@ +package com.saulmm.material.fragments; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.saulmm.material.R; + + +public class NavigationFragment extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + return inflater.inflate(R.layout.fragment_navigation_drawer, container, false); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/saulmm/material/fragments/SamplePageFragment.java b/app/src/main/java/com/saulmm/material/fragments/SamplePageFragment.java new file mode 100644 index 0000000..5bc38e2 --- /dev/null +++ b/app/src/main/java/com/saulmm/material/fragments/SamplePageFragment.java @@ -0,0 +1,22 @@ +package com.saulmm.material.fragments; + +import android.app.Fragment; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.saulmm.material.R; + +public class SamplePageFragment extends Fragment { + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + View rootView = inflater.inflate(R.layout.fragment_dialer_sample_page, container); + + return rootView; + } +} diff --git a/app/src/main/java/com/saulmm/material/fragments/SlidingTabFragment.java b/app/src/main/java/com/saulmm/material/fragments/SlidingTabFragment.java new file mode 100644 index 0000000..b50dd02 --- /dev/null +++ b/app/src/main/java/com/saulmm/material/fragments/SlidingTabFragment.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 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.saulmm.material.fragments; + + +import android.app.Fragment; +import android.os.Bundle; +import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.saulmm.material.R; +import com.saulmm.material.views.adapters.SamplePagerAdapter; +import com.saulmm.material.views.widgets.SlidingTabLayout; + + +public class SlidingTabFragment extends Fragment { + + static final String LOG_TAG = "SlidingTabsBasicFragment"; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + return inflater.inflate(R.layout.fragment_dialer_sliding, container, false); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + + ViewPager mViewPager = (ViewPager) view.findViewById(R.id.viewpager); + mViewPager.setAdapter(new SamplePagerAdapter(getActivity())); + + SlidingTabLayout mSlidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs); + mSlidingTabLayout.setViewPager(mViewPager); + } +} diff --git a/app/src/main/java/com/saulmm/material/utils/AnimatorAdapter.java b/app/src/main/java/com/saulmm/material/utils/AnimatorAdapter.java new file mode 100644 index 0000000..4de2736 --- /dev/null +++ b/app/src/main/java/com/saulmm/material/utils/AnimatorAdapter.java @@ -0,0 +1,25 @@ +package com.saulmm.material.utils; + +import android.animation.Animator; + +public class AnimatorAdapter implements Animator.AnimatorListener { + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } +} diff --git a/app/src/main/java/com/saulmm/material/utils/GUIUtils.java b/app/src/main/java/com/saulmm/material/utils/GUIUtils.java new file mode 100644 index 0000000..4034769 --- /dev/null +++ b/app/src/main/java/com/saulmm/material/utils/GUIUtils.java @@ -0,0 +1,104 @@ +package com.saulmm.material.utils; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.content.Context; +import android.graphics.Outline; +import android.transition.Fade; +import android.view.View; +import android.view.ViewAnimationUtils; +import android.view.ViewPropertyAnimator; +import android.view.Window; +import android.view.animation.PathInterpolator; + +import com.saulmm.material.R; + +public class GUIUtils { + + public static final int SCALE_FACTOR = 30; + + public static void configureWindowEnterExitExplodeTransition(Window w) { + + Fade ex = new Fade(); + ex.setInterpolator(new PathInterpolator(0.4f, 0, 1, 1)); + ex.setDuration(5000); + w.setExitTransition(ex); + w.setEnterTransition(ex); + } + + public static void configureFab (View fabButton) { + + int fabSize = fabButton.getContext().getResources() + .getDimensionPixelSize(R.dimen.fab_size); + Outline fabOutLine = new Outline(); + fabOutLine.setOval(0, 0, fabSize, fabSize); + } + + public static int getStatusBarHeight(Context c) { + + int result = 0; + int resourceId = c.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + result = c.getResources().getDimensionPixelSize(resourceId); + } + return result; + } + + + public static void hideRevealEffect (final View v, int centerX, int centerY, int initialRadius) { + + v.setVisibility(View.VISIBLE); + + // create the animation (the final radius is zero) + Animator anim = ViewAnimationUtils.createCircularReveal( + v, centerX, centerY, initialRadius, 0); + + anim.setDuration(350); + + // make the view invisible when the animation is done + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + v.setVisibility(View.INVISIBLE); + } + }); + + anim.start(); + } + + public static void showRevealEffect(final View v, int centerX, int centerY, Animator.AnimatorListener lis) { + + v.setVisibility(View.VISIBLE); + + int height = v.getHeight(); + + Animator anim = ViewAnimationUtils.createCircularReveal( + v, centerX, centerY, 0, height); + + anim.setDuration(350); + + anim.addListener(lis); + anim.start(); + } + + + + public static void hideViewByScale(View view) { + + ViewPropertyAnimator propertyAnimator = view.animate().setStartDelay(SCALE_FACTOR) + .scaleX(0).scaleY(0); + + propertyAnimator.start(); + } + + public static void showViewByScale(View view) { + + ViewPropertyAnimator propertyAnimator = view.animate().setStartDelay(SCALE_FACTOR) + .scaleX(1).scaleY(1); + + propertyAnimator.start(); + } + + +} diff --git a/app/src/main/java/com/saulmm/material/utils/TransitionAdapter.java b/app/src/main/java/com/saulmm/material/utils/TransitionAdapter.java new file mode 100644 index 0000000..1297bb8 --- /dev/null +++ b/app/src/main/java/com/saulmm/material/utils/TransitionAdapter.java @@ -0,0 +1,31 @@ +package com.saulmm.material.utils; + +import android.transition.Transition; + +public class TransitionAdapter implements Transition.TransitionListener { + + @Override + public void onTransitionStart(Transition transition) { + + } + + @Override + public void onTransitionEnd(Transition transition) { + + } + + @Override + public void onTransitionCancel(Transition transition) { + + } + + @Override + public void onTransitionPause(Transition transition) { + + } + + @Override + public void onTransitionResume(Transition transition) { + + } +} diff --git a/app/src/main/java/com/saulmm/material/views/adapters/SamplePagerAdapter.java b/app/src/main/java/com/saulmm/material/views/adapters/SamplePagerAdapter.java new file mode 100644 index 0000000..e46a832 --- /dev/null +++ b/app/src/main/java/com/saulmm/material/views/adapters/SamplePagerAdapter.java @@ -0,0 +1,53 @@ +package com.saulmm.material.views.adapters; + +import android.content.Context; +import android.support.v4.view.PagerAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.saulmm.material.R; + +@SuppressWarnings("FieldCanBeLocal") +public class SamplePagerAdapter extends PagerAdapter { + + private final String [] TITLES = {"SPEED DIAL", "RECENTS", "CONTACTS"}; + private final int FRAGMENT_COUNT = 3; + private Context context; + + public SamplePagerAdapter(Context context) { + this.context = context; + } + + @Override + public int getCount() { + return FRAGMENT_COUNT; + } + + @Override + public boolean isViewFromObject(View view, Object o) { + return o == view; + } + + + @Override + public CharSequence getPageTitle(int position) { + return TITLES[position]; + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + + View view = LayoutInflater.from(context).inflate( + R.layout.fragment_dialer_sample_page, + container, false); + + container.addView(view); + return view; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + container.removeView((View) object); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/saulmm/material/views/widgets/SlidingTabLayout.java b/app/src/main/java/com/saulmm/material/views/widgets/SlidingTabLayout.java new file mode 100644 index 0000000..dbf48ad --- /dev/null +++ b/app/src/main/java/com/saulmm/material/views/widgets/SlidingTabLayout.java @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2013 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.saulmm.material.views.widgets; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Build; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.HorizontalScrollView; +import android.widget.TextView; + +import com.saulmm.material.R; + +import java.util.ArrayList; + +/** + * To be used with ViewPager to provide a tab indicator component which give constant feedback as to + * the user's scroll progress. + *

+ * To use the component, simply add it to your view hierarchy. Then in your + * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call + * {@link #setViewPager(android.support.v4.view.ViewPager)} providing it the ViewPager this layout is being used for. + *

+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors + * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The + * alternative is via the {@link SlidingTabLayout.TabColorizer} interface which provides you complete control over + * which color is used for any individual position. + *

+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, + * providing the layout ID of your custom layout. + */ +public class SlidingTabLayout extends HorizontalScrollView { + + /** + * Allows complete control over the colors drawn in the tab layout. Set with + * {@link #setCustomTabColorizer(SlidingTabLayout.TabColorizer)}. + */ + public interface TabColorizer { + + /** + * @return return the color of the indicator used when {@code position} is selected. + */ + int getIndicatorColor(int position); + + /** + * @return return the color of the divider drawn to the right of {@code position}. + */ + int getDividerColor(int position); + + } + + private static final int TITLE_OFFSET_DIPS = 24; + private static final int TAB_VIEW_PADDING_DIPS = 16; + private static final int TAB_VIEW_TEXT_SIZE_SP = 14; + + private int mTitleOffset; + + private int mTabViewLayoutId; + private int mTabViewTextViewId; + + private ViewPager mViewPager; + private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; + + private ArrayList pagerListeners = new ArrayList(); + + private final SlidingTabStrip mTabStrip; + + public SlidingTabLayout(Context context) { + this(context, null); + } + + public SlidingTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + // Disable the Scroll Bar + setHorizontalScrollBarEnabled(false); + // Make sure that the Tab Strips fills this View + setFillViewport(true); + + mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); + + mTabStrip = new SlidingTabStrip(context); + addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + + pagerListeners.add(new InternalViewPagerListener()); + } + + public void addPagerListener (ViewPager.OnPageChangeListener listener) { + pagerListeners.add(listener); + } + + /** + * Set the custom {@link SlidingTabLayout.TabColorizer} to be used. + * + * If you only require simple custmisation then you can use + * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve + * similar effects. + */ + public void setCustomTabColorizer(TabColorizer tabColorizer) { + mTabStrip.setCustomTabColorizer(tabColorizer); + } + + /** + * Sets the colors to be used for indicating the selected tab. These colors are treated as a + * circular array. Providing one color will mean that all tabs are indicated with the same color. + */ + public void setSelectedIndicatorColors(int... colors) { + mTabStrip.setSelectedIndicatorColors(colors); + } + + /** + * Sets the colors to be used for tab dividers. These colors are treated as a circular array. + * Providing one color will mean that all tabs are indicated with the same color. + */ + public void setDividerColors(int... colors) { + mTabStrip.setDividerColors(colors); + } + + /** + * Set the {@link android.support.v4.view.ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are + * required to set any {@link android.support.v4.view.ViewPager.OnPageChangeListener} through this method. This is so + * that the layout can update it's scroll position correctly. + * + * @see android.support.v4.view.ViewPager#setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener) + */ + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { + mViewPagerPageChangeListener = listener; + } + + /** + * Set the custom layout to be inflated for the tab views. + * + * @param layoutResId Layout id to be inflated + * @param textViewId id of the {@link android.widget.TextView} in the inflated view + */ + public void setCustomTabView(int layoutResId, int textViewId) { + mTabViewLayoutId = layoutResId; + mTabViewTextViewId = textViewId; + } + + /** + * Sets the associated view pager. Note that the assumption here is that the pager content + * (number of tabs and tab titles) does not change after this call has been made. + */ + public void setViewPager(ViewPager viewPager) { + mTabStrip.removeAllViews(); + + mViewPager = viewPager; + if (viewPager != null) { + viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int i, float v, int i2) { + + for (ViewPager.OnPageChangeListener pagerListener : pagerListeners) { + pagerListener.onPageScrolled(i, v, i2); + } + } + + @Override + public void onPageSelected(int i) { + for (ViewPager.OnPageChangeListener pagerListener : pagerListeners) { + pagerListener.onPageSelected(i); + } + } + + @Override + public void onPageScrollStateChanged(int i) { + for (ViewPager.OnPageChangeListener pagerListener : pagerListeners) { + pagerListener.onPageScrollStateChanged(i); + } + } + }); + populateTabStrip(); + } + } + + /** + * Create a default view to be used for tabs. This is called if a custom tab view is not set via + * {@link #setCustomTabView(int, int)}. + */ + protected TextView createDefaultTabView(Context context) { + TextView textView = new TextView(context); + textView.setGravity(Gravity.CENTER); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); + textView.setTypeface(Typeface.DEFAULT_BOLD); + textView.setTextAppearance(context, R.style.SlidingTextViewStyle); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // If we're running on Honeycomb or newer, then we can use the Theme's + // selectableItemBackground to ensure that the View has a pressed state + TypedValue outValue = new TypedValue(); + getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, + outValue, true); + textView.setBackgroundResource(outValue.resourceId); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style + textView.setAllCaps(true); + } + + int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); + textView.setPadding(padding, padding, padding, padding); + + return textView; + } + + private void populateTabStrip() { + final PagerAdapter adapter = mViewPager.getAdapter(); + final OnClickListener tabClickListener = new TabClickListener(); + + for (int i = 0; i < adapter.getCount(); i++) { + View tabView = null; + TextView tabTitleView = null; + + if (mTabViewLayoutId != 0) { + // If there is a custom tab view layout id set, try and inflate it + tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, + false); + tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); + } + + if (tabView == null) { + tabView = createDefaultTabView(getContext()); + } + + if (tabTitleView == null && TextView.class.isInstance(tabView)) { + tabTitleView = (TextView) tabView; + } + + tabTitleView.setText(adapter.getPageTitle(i)); + tabView.setOnClickListener(tabClickListener); + + mTabStrip.addView(tabView); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mViewPager != null) { + scrollToTab(mViewPager.getCurrentItem(), 0); + } + } + + private void scrollToTab(int tabIndex, int positionOffset) { + final int tabStripChildCount = mTabStrip.getChildCount(); + if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { + return; + } + + View selectedChild = mTabStrip.getChildAt(tabIndex); + if (selectedChild != null) { + int targetScrollX = selectedChild.getLeft() + positionOffset; + + if (tabIndex > 0 || positionOffset > 0) { + // If we're not at the first child and are mid-scroll, make sure we obey the offset + targetScrollX -= mTitleOffset; + } + + scrollTo(targetScrollX, 0); + } + } + + private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { + private int mScrollState; + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + int tabStripChildCount = mTabStrip.getChildCount(); + if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { + return; + } + + mTabStrip.onViewPagerPageChanged(position, positionOffset); + + View selectedTitle = mTabStrip.getChildAt(position); + int extraOffset = (selectedTitle != null) + ? (int) (positionOffset * selectedTitle.getWidth()) + : 0; + scrollToTab(position, extraOffset); + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, + positionOffsetPixels); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + mScrollState = state; + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrollStateChanged(state); + } + } + + @Override + public void onPageSelected(int position) { + if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { + mTabStrip.onViewPagerPageChanged(position, 0f); + scrollToTab(position, 0); + } + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageSelected(position); + } + } + + } + + private class TabClickListener implements OnClickListener { + @Override + public void onClick(View v) { + for (int i = 0; i < mTabStrip.getChildCount(); i++) { + if (v == mTabStrip.getChildAt(i)) { + mViewPager.setCurrentItem(i); + return; + } + } + } + } + +} diff --git a/app/src/main/java/com/saulmm/material/views/widgets/SlidingTabStrip.java b/app/src/main/java/com/saulmm/material/views/widgets/SlidingTabStrip.java new file mode 100644 index 0000000..50084af --- /dev/null +++ b/app/src/main/java/com/saulmm/material/views/widgets/SlidingTabStrip.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2013 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.saulmm.material.views.widgets; + +import android.R; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.widget.LinearLayout; + +class SlidingTabStrip extends LinearLayout { + + private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2; + private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; + private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3; + private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5; + + private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1; + private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20; + private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f; + + private final int mBottomBorderThickness; + private final Paint mBottomBorderPaint; + + private final int mSelectedIndicatorThickness; + private final Paint mSelectedIndicatorPaint; + + private final int mDefaultBottomBorderColor; + + private final Paint mDividerPaint; + private final float mDividerHeight; + + private int mSelectedPosition; + private float mSelectionOffset; + + private SlidingTabLayout.TabColorizer mCustomTabColorizer; + private final SimpleTabColorizer mDefaultTabColorizer; + + SlidingTabStrip(Context context) { + this(context, null); + } + + SlidingTabStrip(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + + final float density = getResources().getDisplayMetrics().density; + + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true); + final int themeForegroundColor = outValue.data; + + TypedValue backgroundValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.colorPrimary, outValue, true); + final int themeBackground = outValue.data; + + TypedValue accentValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.colorAccent, outValue, true); + final int accentColor = outValue.data; + + mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor, + DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); + + mDefaultTabColorizer = new SimpleTabColorizer(); + mDefaultTabColorizer.setIndicatorColors(accentColor); + mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor, + DEFAULT_DIVIDER_COLOR_ALPHA)); + + mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); + mBottomBorderPaint = new Paint(); + mBottomBorderPaint.setColor(mDefaultBottomBorderColor); + + mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); + mSelectedIndicatorPaint = new Paint(); + + mDividerHeight = DEFAULT_DIVIDER_HEIGHT; + mDividerPaint = new Paint(); + mDividerPaint.setStrokeWidth((int) (0)); + + setBackgroundColor(themeBackground); + } + + void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { + mCustomTabColorizer = customTabColorizer; + invalidate(); + } + + void setSelectedIndicatorColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setIndicatorColors(colors); + invalidate(); + } + + void setDividerColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setDividerColors(colors); + invalidate(); + } + + void onViewPagerPageChanged(int position, float positionOffset) { + mSelectedPosition = position; + mSelectionOffset = positionOffset; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + final int height = getHeight(); + final int childCount = getChildCount(); + final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height); + final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null + ? mCustomTabColorizer + : mDefaultTabColorizer; + + // Thick colored underline below the current selection + if (childCount > 0) { + View selectedTitle = getChildAt(mSelectedPosition); + int left = selectedTitle.getLeft(); + int right = selectedTitle.getRight(); + int color = tabColorizer.getIndicatorColor(mSelectedPosition); + + if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { + int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1); + if (color != nextColor) { + color = blendColors(nextColor, color, mSelectionOffset); + } + + // Draw the selection partway between the tabs + View nextTitle = getChildAt(mSelectedPosition + 1); + left = (int) (mSelectionOffset * nextTitle.getLeft() + + (1.0f - mSelectionOffset) * left); + right = (int) (mSelectionOffset * nextTitle.getRight() + + (1.0f - mSelectionOffset) * right); + } + + mSelectedIndicatorPaint.setColor(color); + + canvas.drawRect(left, height - mSelectedIndicatorThickness, right, + height, mSelectedIndicatorPaint); + } + + // Thin underline along the entire bottom edge + canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); + + // Vertical separators between the titles + int separatorTop = (height - dividerHeightPx) / 2; + for (int i = 0; i < childCount - 1; i++) { + View child = getChildAt(i); + mDividerPaint.setColor(tabColorizer.getDividerColor(i)); + canvas.drawLine(child.getRight(), separatorTop, child.getRight(), + separatorTop + dividerHeightPx, mDividerPaint); + } + } + + /** + * Set the alpha value of the {@code color} to be the given {@code alpha} value. + */ + private static int setColorAlpha(int color, byte alpha) { + return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); + } + + /** + * Blend {@code color1} and {@code color2} using the given ratio. + * + * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, + * 0.0 will return {@code color2}. + */ + private static int blendColors(int color1, int color2, float ratio) { + final float inverseRation = 1f - ratio; + float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation); + float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation); + float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation); + return Color.rgb((int) r, (int) g, (int) b); + } + + private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { + private int[] mIndicatorColors; + private int[] mDividerColors; + + @Override + public final int getIndicatorColor(int position) { + return mIndicatorColors[position % mIndicatorColors.length]; + } + + @Override + public final int getDividerColor(int position) { + return mDividerColors[position % mDividerColors.length]; + } + + void setIndicatorColors(int... colors) { + mIndicatorColors = colors; + } + + void setDividerColors(int... colors) { + mDividerColors = colors; + } + } +} \ No newline at end of file diff --git a/app/src/main/res/anim/translate_down_off.xml b/app/src/main/res/anim/translate_down_off.xml new file mode 100644 index 0000000..79302d0 --- /dev/null +++ b/app/src/main/res/anim/translate_down_off.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/app/src/main/res/anim/translate_down_on.xml b/app/src/main/res/anim/translate_down_on.xml new file mode 100644 index 0000000..b534991 --- /dev/null +++ b/app/src/main/res/anim/translate_down_on.xml @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable-hdpi/drawer_shadow.9.png b/app/src/main/res/drawable-hdpi/drawer_shadow.9.png new file mode 100644 index 0000000..236bff5 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/drawer_shadow.9.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_drawer.png b/app/src/main/res/drawable-hdpi/ic_drawer.png new file mode 100644 index 0000000..c59f601 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_drawer.png differ diff --git a/app/src/main/res/drawable-mdpi/drawer_shadow.9.png b/app/src/main/res/drawable-mdpi/drawer_shadow.9.png new file mode 100644 index 0000000..ffe3a28 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/drawer_shadow.9.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_drawer.png b/app/src/main/res/drawable-mdpi/ic_drawer.png new file mode 100644 index 0000000..1ed2c56 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_drawer.png differ diff --git a/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png b/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png new file mode 100644 index 0000000..fabe9d9 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_drawer.png b/app/src/main/res/drawable-xhdpi/ic_drawer.png new file mode 100644 index 0000000..a5fa74d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_drawer.png differ diff --git a/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png b/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png new file mode 100644 index 0000000..b91e9d7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_drawer.png b/app/src/main/res/drawable-xxhdpi/ic_drawer.png new file mode 100644 index 0000000..9c4685d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_drawer.png differ diff --git a/app/src/main/res/drawable/color1.xml b/app/src/main/res/drawable/color1.xml new file mode 100644 index 0000000..b336be9 --- /dev/null +++ b/app/src/main/res/drawable/color1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/color2.xml b/app/src/main/res/drawable/color2.xml new file mode 100644 index 0000000..12e117e --- /dev/null +++ b/app/src/main/res/drawable/color2.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/color3.xml b/app/src/main/res/drawable/color3.xml new file mode 100644 index 0000000..c0cf05c --- /dev/null +++ b/app/src/main/res/drawable/color3.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/color4.xml b/app/src/main/res/drawable/color4.xml new file mode 100644 index 0000000..445b2ca --- /dev/null +++ b/app/src/main/res/drawable/color4.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ripple_dialer_call.xml b/app/src/main/res/drawable/ripple_dialer_call.xml new file mode 100644 index 0000000..87d6c96 --- /dev/null +++ b/app/src/main/res/drawable/ripple_dialer_call.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ripple_dialer_idle.xml b/app/src/main/res/drawable/ripple_dialer_idle.xml new file mode 100644 index 0000000..ead7fa8 --- /dev/null +++ b/app/src/main/res/drawable/ripple_dialer_idle.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/translation_selector.xml b/app/src/main/res/drawable/translation_selector.xml new file mode 100644 index 0000000..dc4a668 --- /dev/null +++ b/app/src/main/res/drawable/translation_selector.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_color.xml b/app/src/main/res/layout/activity_color.xml new file mode 100644 index 0000000..78816d1 --- /dev/null +++ b/app/src/main/res/layout/activity_color.xml @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/color.xml b/app/src/main/res/menu/color.xml new file mode 100644 index 0000000..3428565 --- /dev/null +++ b/app/src/main/res/menu/color.xml @@ -0,0 +1,8 @@ +

+ + diff --git a/app/src/main/res/menu/menu_elevation_sample.xml b/app/src/main/res/menu/menu_elevation_sample.xml new file mode 100644 index 0000000..31e169c --- /dev/null +++ b/app/src/main/res/menu/menu_elevation_sample.xml @@ -0,0 +1,8 @@ + + + diff --git a/app/src/main/res/menu/my_activity2.xml b/app/src/main/res/menu/my_activity2.xml index 7ef86c1..84b1d89 100644 --- a/app/src/main/res/menu/my_activity2.xml +++ b/app/src/main/res/menu/my_activity2.xml @@ -1,6 +1,6 @@ + tools:context="com.saulmm.material.activities.TransitionSecondActivity" > + + + diff --git a/app/src/main/res/transition/circular_reveal.xml b/app/src/main/res/transition/circular_reveal.xml new file mode 100644 index 0000000..b80d24b --- /dev/null +++ b/app/src/main/res/transition/circular_reveal.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/transition/move_resize_image.xml b/app/src/main/res/transition/move_resize_image.xml new file mode 100644 index 0000000..e2df150 --- /dev/null +++ b/app/src/main/res/transition/move_resize_image.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml index 5333630..3a30fbe 100644 --- a/app/src/main/res/values-v21/styles.xml +++ b/app/src/main/res/values-v21/styles.xml @@ -1,17 +1,64 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 7af9704..409abcc 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -2,9 +2,32 @@ - #00BCD4 - #0097A7 + #F48FB1 + #EC407A #2A2A2A #EEFF41 + #ff515252 + + + + #00C853 + #0277BD + #0288D1 + #FFF + + + #4fc3f7 + #009688 + + #42bd41 + #673AB7 + + #ffb74d + #E91E63 + + #ff8a65 + #d500f9 + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 29405a1..d4bacff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,6 +5,27 @@ Settings MyActivity2 Example action - Hello world! + NavDrawerActivity + ColorActivity + Open + Close + + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + * + 0 + # + + + ElevationSampleActivity diff --git a/build.gradle b/build.gradle index 9b8abe4..d3ff69d 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:0.12.2' + classpath 'com.android.tools.build:gradle:1.1.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle.properties b/gradle.properties index 5d08ba7..1d3591c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: -# Settings specified in this file will override any Gradle settings -# configured through the IDE. +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..8c0fb64 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..0c71e76 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega