├── .gitignore ├── .idea ├── codeStyleSettings.xml ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── Material.iml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── FreeSans.ttf │ ├── java │ └── com │ │ └── rey │ │ └── material │ │ ├── app │ │ ├── ContactEditText.java │ │ ├── ContactView.java │ │ ├── Recipient.java │ │ ├── Recurring.java │ │ ├── RecurringPickerDialog.java │ │ └── WeekView.java │ │ └── demo │ │ ├── ButtonFragment.java │ │ ├── CustomViewPager.java │ │ ├── DemoApplication.java │ │ ├── DialogsFragment.java │ │ ├── FabFragment.java │ │ ├── MainActivity.java │ │ ├── ProgressFragment.java │ │ ├── SliderFragment.java │ │ ├── SnackbarFragment.java │ │ ├── SpinnersFragment.java │ │ ├── SwitchesFragment.java │ │ └── TextfieldFragment.java │ └── res │ ├── anim │ ├── anim_scale_in.xml │ └── anim_scale_out.xml │ ├── drawable-xhdpi │ ├── bg_bt_raise.9.png │ ├── bg_bt_raise_color.9.png │ ├── bg_bt_raise_dark.9.png │ ├── bg_toolbar_dark.xml │ ├── bg_toolbar_light.xml │ ├── ic_autorenew_white_24dp.png │ ├── ic_cancel_white_24dp.png │ ├── ic_done_all_white_24dp.png │ ├── ic_done_white_24dp.png │ ├── ic_launcher.png │ ├── ic_user.png │ ├── ic_visibility_white_24dp.png │ └── popup_background_dark.9.png │ ├── drawable │ ├── bg_window_dark.xml │ ├── bg_window_light.xml │ ├── color_divider_dark.xml │ ├── color_divider_light.xml │ ├── color_label_dark.xml │ ├── color_label_light.xml │ ├── color_switch_dark.xml │ ├── color_thumb_dark.xml │ ├── color_thumb_light.xml │ ├── color_tpi_dark.xml │ ├── color_tpi_light.xml │ ├── color_track_dark.xml │ ├── color_track_light.xml │ └── color_wv_text.xml │ ├── layout │ ├── activity_main.xml │ ├── dialog_recurring.xml │ ├── fragment_button.xml │ ├── fragment_dialog.xml │ ├── fragment_fab.xml │ ├── fragment_progress.xml │ ├── fragment_slider.xml │ ├── fragment_snackbar.xml │ ├── fragment_spinner.xml │ ├── fragment_switches.xml │ ├── fragment_textfield.xml │ ├── layout_dialog_custom.xml │ ├── rd_item_dropdown_end.xml │ ├── rd_item_dropdown_mode.xml │ ├── rd_item_end.xml │ ├── rd_item_mode.xml │ ├── row_contact_replace.xml │ ├── row_contact_selected.xml │ ├── row_drawer.xml │ ├── row_spn.xml │ ├── row_spn_dropdown.xml │ └── view_bottomsheet.xml │ ├── menu │ └── menu_main.xml │ ├── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ ├── styles.xml │ ├── styles_dark.xml │ ├── styles_light.xml │ └── themes.xml │ └── xml │ └── fab_icon_states.xml ├── build.gradle ├── gradle.properties ├── gradle ├── local.properties └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── image ├── bottomsheet.gif ├── button_flat_touch.gif ├── button_flat_wave.gif ├── button_raise_touch.gif ├── button_raise_wave.gif ├── cb.gif ├── dialog_1.png ├── dialog_2.png ├── dialog_3.png ├── dialog_4.png ├── fab_image.gif ├── fab_line.gif ├── progress_circular_determinate.gif ├── progress_circular_indeterminate.gif ├── progress_linear_buffer.gif ├── progress_linear_determinate.gif ├── progress_linear_indeterminate.gif ├── progress_linear_query.gif ├── rb.gif ├── slider_continuous.gif ├── slider_discrete.gif ├── snackbar.png ├── spn.gif ├── switch.gif ├── textfield.gif ├── theme.gif └── tpi.gif ├── material ├── .gitignore ├── bintray.gradle ├── build.gradle ├── install.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ ├── androidx │ │ └── appcompat │ │ │ └── widget │ │ │ └── ListViewCompat.java │ └── com │ │ └── rey │ │ └── material │ │ ├── app │ │ ├── BottomSheetDialog.java │ │ ├── DatePickerDialog.java │ │ ├── Dialog.java │ │ ├── DialogFragment.java │ │ ├── SimpleDialog.java │ │ ├── ThemeManager.java │ │ ├── TimePickerDialog.java │ │ └── ToolbarManager.java │ │ ├── drawable │ │ ├── ArrowDrawable.java │ │ ├── BlankDrawable.java │ │ ├── CheckBoxDrawable.java │ │ ├── CircleDrawable.java │ │ ├── CircularProgressDrawable.java │ │ ├── ContactChipDrawable.java │ │ ├── DividerDrawable.java │ │ ├── LineMorphingDrawable.java │ │ ├── LinearProgressDrawable.java │ │ ├── NavigationDrawerDrawable.java │ │ ├── OvalShadowDrawable.java │ │ ├── PaddingDrawable.java │ │ ├── RadioButtonDrawable.java │ │ ├── RevealDrawable.java │ │ ├── RippleDrawable.java │ │ ├── ThemeDrawable.java │ │ └── ToolbarRippleDrawable.java │ │ ├── text │ │ └── style │ │ │ └── ContactChipSpan.java │ │ ├── util │ │ ├── ColorUtil.java │ │ ├── ThemeUtil.java │ │ ├── TypefaceUtil.java │ │ └── ViewUtil.java │ │ └── widget │ │ ├── Button.java │ │ ├── CheckBox.java │ │ ├── CheckedImageView.java │ │ ├── CheckedTextView.java │ │ ├── CircleCheckedTextView.java │ │ ├── CompoundButton.java │ │ ├── DatePicker.java │ │ ├── EditText.java │ │ ├── FloatingActionButton.java │ │ ├── FrameLayout.java │ │ ├── ImageButton.java │ │ ├── ImageView.java │ │ ├── LinearLayout.java │ │ ├── ListPopupWindow.java │ │ ├── ListView.java │ │ ├── PopupWindow.java │ │ ├── ProgressView.java │ │ ├── RadioButton.java │ │ ├── RelativeLayout.java │ │ ├── RippleManager.java │ │ ├── Slider.java │ │ ├── SnackBar.java │ │ ├── Spinner.java │ │ ├── Switch.java │ │ ├── TabIndicatorView.java │ │ ├── TabPageIndicator.java │ │ ├── TextView.java │ │ ├── TimePicker.java │ │ └── YearPicker.java │ └── res │ ├── values │ ├── attrs.xml │ └── styles.xml │ └── xml │ └── nav_states.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Material.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion "28.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.rey.material.demo" 9 | minSdkVersion 14 10 | targetSdkVersion 28 11 | versionCode 7 12 | versionName "0.0.7" 13 | } 14 | 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | 22 | if(project.hasProperty('MATERIAL_KEYSTORE_FILE')) { 23 | android.signingConfigs { 24 | release { 25 | storeFile file(MATERIAL_KEYSTORE_FILE) 26 | storePassword MATERIAL_KEYSTORE_PASSWORD 27 | keyAlias MATERIAL_KEYSTORE_ALIAS 28 | keyPassword MATERIAL_KEYSTORE_PASSWORD 29 | } 30 | } 31 | 32 | android.buildTypes.release.signingConfig signingConfigs.release 33 | } 34 | } 35 | 36 | ext{ 37 | androidXVersion = '1.0.0' 38 | } 39 | 40 | dependencies { 41 | implementation fileTree(dir: 'libs', include: ['*.jar']) 42 | implementation "androidx.appcompat:appcompat:${androidXVersion}" 43 | implementation "androidx.cardview:cardview:${androidXVersion}" 44 | implementation "androidx.recyclerview:recyclerview:${androidXVersion}" 45 | implementation 'com.squareup.picasso:picasso:2.5.0' 46 | implementation 'com.squareup.leakcanary:leakcanary-android:1.3.1' 47 | implementation project(':material') 48 | } 49 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\vietph\android\adt-bundle-windows-x86_64-20140702\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/assets/FreeSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/app/src/main/assets/FreeSans.ttf -------------------------------------------------------------------------------- /app/src/main/java/com/rey/material/app/Recipient.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.app; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * Created by Rey on 3/2/2015. 8 | */ 9 | public class Recipient implements Parcelable{ 10 | String name; 11 | String number; 12 | String lookupKey; 13 | 14 | public Recipient() { 15 | } 16 | 17 | private Recipient(Parcel in) { 18 | this.name = in.readString(); 19 | this.number = in.readString(); 20 | this.lookupKey = in.readString(); 21 | } 22 | 23 | @Override 24 | public String toString(){ 25 | return Recipient.class.getSimpleName() + "[name = " + name + ", number = " + number + ", key = " + lookupKey + "]"; 26 | } 27 | 28 | 29 | @Override 30 | public int describeContents() { 31 | return 0; 32 | } 33 | 34 | @Override 35 | public void writeToParcel(Parcel dest, int flags) { 36 | dest.writeString(this.name); 37 | dest.writeString(this.number); 38 | dest.writeString(this.lookupKey); 39 | } 40 | 41 | public static final Creator CREATOR = new Creator() { 42 | public Recipient createFromParcel(Parcel source) { 43 | return new Recipient(source); 44 | } 45 | 46 | public Recipient[] newArray(int size) { 47 | return new Recipient[size]; 48 | } 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/rey/material/demo/ButtonFragment.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.demo; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import androidx.fragment.app.Fragment; 11 | 12 | import com.rey.material.widget.Button; 13 | import com.rey.material.widget.FloatingActionButton; 14 | 15 | public class ButtonFragment extends Fragment { 16 | 17 | public static ButtonFragment newInstance(){ 18 | ButtonFragment fragment = new ButtonFragment(); 19 | 20 | return fragment; 21 | } 22 | 23 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 24 | @Override 25 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 26 | View v = inflater.inflate(R.layout.fragment_button, container, false); 27 | 28 | Button bt_flat = (Button)v.findViewById(R.id.button_bt_flat); 29 | Button bt_flat_color = (Button)v.findViewById(R.id.button_bt_flat_color); 30 | Button bt_flat_wave = (Button)v.findViewById(R.id.button_bt_flat_wave); 31 | Button bt_flat_wave_color = (Button)v.findViewById(R.id.button_bt_flat_wave_color); 32 | Button bt_raise = (Button)v.findViewById(R.id.button_bt_raise); 33 | Button bt_raise_color = (Button)v.findViewById(R.id.button_bt_raise_color); 34 | Button bt_raise_wave = (Button)v.findViewById(R.id.button_bt_raise_wave); 35 | Button bt_raise_wave_color = (Button)v.findViewById(R.id.button_bt_raise_wave_color); 36 | FloatingActionButton bt_float = (FloatingActionButton)v.findViewById(R.id.button_bt_float); 37 | FloatingActionButton bt_float_color = (FloatingActionButton)v.findViewById(R.id.button_bt_float_color); 38 | FloatingActionButton bt_float_wave = (FloatingActionButton)v.findViewById(R.id.button_bt_float_wave); 39 | FloatingActionButton bt_float_wave_color = (FloatingActionButton)v.findViewById(R.id.button_bt_float_wave_color); 40 | 41 | View.OnClickListener listener = new View.OnClickListener() { 42 | 43 | @Override 44 | public void onClick(View v) { 45 | if(v instanceof FloatingActionButton){ 46 | FloatingActionButton bt = (FloatingActionButton)v; 47 | bt.setLineMorphingState((bt.getLineMorphingState() + 1) % 2, true); 48 | } 49 | 50 | // System.out.println(v + " " + ((RippleDrawable)v.getBackground()).getDelayClickType()); 51 | } 52 | }; 53 | 54 | View.OnClickListener listener_delay = new View.OnClickListener() { 55 | 56 | @Override 57 | public void onClick(View v) { 58 | if(v instanceof FloatingActionButton){ 59 | FloatingActionButton bt = (FloatingActionButton)v; 60 | bt.setLineMorphingState((bt.getLineMorphingState() + 1) % 2, true); 61 | } 62 | 63 | // System.out.println(v + " " + ((RippleDrawable)v.getBackground()).getDelayClickType()); 64 | } 65 | }; 66 | 67 | bt_flat.setOnClickListener(listener); 68 | bt_flat_wave.setOnClickListener(listener); 69 | bt_raise.setOnClickListener(listener); 70 | bt_raise_wave.setOnClickListener(listener); 71 | bt_float.setOnClickListener(listener); 72 | bt_float_wave.setOnClickListener(listener); 73 | 74 | bt_flat_color.setOnClickListener(listener_delay); 75 | bt_flat_wave_color.setOnClickListener(listener_delay); 76 | bt_raise_color.setOnClickListener(listener_delay); 77 | bt_raise_wave_color.setOnClickListener(listener_delay); 78 | bt_float_color.setOnClickListener(listener_delay); 79 | bt_float_wave_color.setOnClickListener(listener_delay); 80 | 81 | // bt_flat.setOnClickListener(new View.OnClickListener(){ 82 | // @Override 83 | // public void onClick(View v) { 84 | // RecurringPickerDialog.Builder builder = new RecurringPickerDialog.Builder(R.style.Material_App_Dialog_Light){ 85 | // @Override 86 | // public void onPositiveActionClicked(DialogFragment fragment) { 87 | // RecurringPickerDialog dialog = (RecurringPickerDialog)fragment.getDialog(); 88 | // Toast.makeText(fragment.getActivity(), dialog.getRecurring().toString(), Toast.LENGTH_LONG).show(); 89 | // fragment.dismiss(); 90 | // } 91 | // 92 | // @Override 93 | // public void onNegativeActionClicked(DialogFragment fragment) { 94 | // Toast.makeText(fragment.getActivity(), "Cancelled", Toast.LENGTH_SHORT).show(); 95 | // fragment.dismiss(); 96 | // } 97 | // }; 98 | // Recurring recurring = new Recurring(); 99 | // recurring.setStartTime(System.currentTimeMillis()); 100 | // recurring.setRepeatMode(Recurring.REPEAT_WEEKLY); 101 | // recurring.setEnabledWeekday(Calendar.SUNDAY, true); 102 | // recurring.setEnabledWeekday(Calendar.TUESDAY, true); 103 | // builder.recurring(recurring) 104 | // .datePickerLayoutStyle(R.style.Material_App_Dialog_DatePicker_Light) 105 | // .positiveAction("OK") 106 | // .negativeAction("CANCEL"); 107 | // 108 | // DialogFragment fragment = DialogFragment.newInstance(builder); 109 | // fragment.show(getFragmentManager(), null); 110 | // } 111 | // }); 112 | 113 | return v; 114 | } 115 | 116 | @Override 117 | public void onPause() { 118 | super.onPause(); 119 | } 120 | 121 | @Override 122 | public void onResume() { 123 | super.onResume(); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/com/rey/material/demo/CustomViewPager.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.demo; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | 7 | import androidx.viewpager.widget.ViewPager; 8 | 9 | import com.rey.material.widget.Slider; 10 | import com.rey.material.widget.Switch; 11 | 12 | /** 13 | * Created by Rey on 3/18/2015. 14 | */ 15 | public class CustomViewPager extends ViewPager { 16 | 17 | public CustomViewPager(Context context, AttributeSet attrs) { 18 | super(context, attrs); 19 | } 20 | 21 | public CustomViewPager(Context context) { 22 | super(context); 23 | } 24 | 25 | protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { 26 | return super.canScroll(v, checkV, dx, x, y) || (checkV && customCanScroll(v)); 27 | } 28 | 29 | protected boolean customCanScroll(View v) { 30 | if (v instanceof Slider || v instanceof Switch) 31 | return true; 32 | return false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/rey/material/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.demo; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.rey.material.app.ThemeManager; 7 | import com.squareup.leakcanary.RefWatcher; 8 | 9 | /** 10 | * Created by Rey on 5/22/2015. 11 | */ 12 | public class DemoApplication extends Application{ 13 | 14 | public static RefWatcher getRefWatcher(Context context) { 15 | DemoApplication application = (DemoApplication) context.getApplicationContext(); 16 | return application.refWatcher; 17 | } 18 | 19 | private RefWatcher refWatcher; 20 | 21 | @Override public void onCreate() { 22 | super.onCreate(); 23 | ThemeManager.init(this, 2, 0, null); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/rey/material/demo/FabFragment.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.demo; 2 | 3 | import android.annotation.TargetApi; 4 | import android.graphics.drawable.Drawable; 5 | import android.os.Build; 6 | import android.os.Bundle; 7 | import androidx.fragment.app.Fragment; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import com.rey.material.widget.FloatingActionButton; 13 | 14 | public class FabFragment extends Fragment{ 15 | 16 | 17 | public static FabFragment newInstance(){ 18 | FabFragment fragment = new FabFragment(); 19 | 20 | return fragment; 21 | } 22 | 23 | private Drawable[] mDrawables = new Drawable[2]; 24 | private int index = 0; 25 | 26 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 27 | @Override 28 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 29 | View v = inflater.inflate(R.layout.fragment_fab, container, false); 30 | 31 | final FloatingActionButton fab_line = (FloatingActionButton)v.findViewById(R.id.fab_line); 32 | fab_line.setOnClickListener(new View.OnClickListener() { 33 | @Override 34 | public void onClick(View v) { 35 | fab_line.setLineMorphingState((fab_line.getLineMorphingState() + 1) % 2, true); 36 | } 37 | }); 38 | 39 | final FloatingActionButton fab_image = (FloatingActionButton)v.findViewById(R.id.fab_image); 40 | mDrawables[0] = v.getResources().getDrawable(R.drawable.ic_autorenew_white_24dp); 41 | mDrawables[1] = v.getResources().getDrawable(R.drawable.ic_done_white_24dp); 42 | fab_image.setIcon(mDrawables[index], false); 43 | fab_image.setOnClickListener(new View.OnClickListener() { 44 | @Override 45 | public void onClick(View v) { 46 | index = (index + 1) % 2; 47 | fab_image.setIcon(mDrawables[index], true); 48 | } 49 | }); 50 | 51 | return v; 52 | } 53 | 54 | @Override 55 | public void onPause() { 56 | super.onPause(); 57 | } 58 | 59 | @Override 60 | public void onResume() { 61 | super.onResume(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/rey/material/demo/ProgressFragment.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.demo; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.os.Handler.Callback; 6 | import android.os.Message; 7 | import androidx.fragment.app.Fragment; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import com.rey.material.widget.ProgressView; 13 | 14 | public class ProgressFragment extends Fragment implements Callback{ 15 | 16 | private ProgressView pv_circular; 17 | private ProgressView pv_circular_colors; 18 | private ProgressView pv_circular_inout; 19 | private ProgressView pv_circular_inout_colors; 20 | private ProgressView pv_circular_determinate_in_out; 21 | private ProgressView pv_circular_determinate; 22 | private ProgressView pv_linear; 23 | private ProgressView pv_linear_colors; 24 | private ProgressView pv_linear_determinate; 25 | private ProgressView pv_linear_query; 26 | private ProgressView pv_linear_buffer; 27 | 28 | private Handler mHandler; 29 | 30 | private static final int MSG_START_PROGRESS = 1000; 31 | private static final int MSG_STOP_PROGRESS = 1001; 32 | private static final int MSG_UPDATE_PROGRESS = 1002; 33 | private static final int MSG_UPDATE_QUERY_PROGRESS = 1003; 34 | private static final int MSG_UPDATE_BUFFER_PROGRESS = 1004; 35 | 36 | private static final long PROGRESS_INTERVAL = 7000; 37 | private static final long START_DELAY = 2000; 38 | private static final long PROGRESS_UPDATE_INTERVAL = PROGRESS_INTERVAL / 100; 39 | private static final long START_QUERY_DELAY = PROGRESS_INTERVAL / 2; 40 | private static final long QUERY_PROGRESS_UPDATE_INTERVAL = (PROGRESS_INTERVAL - START_QUERY_DELAY) / 100; 41 | private static final long BUFFER_PROGRESS_UPDATE_INTERVAL = (PROGRESS_INTERVAL - START_QUERY_DELAY) / 100; 42 | 43 | public static ProgressFragment newInstance(){ 44 | ProgressFragment fragment = new ProgressFragment(); 45 | return fragment; 46 | } 47 | 48 | @Override 49 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 50 | View v = inflater.inflate(R.layout.fragment_progress, container, false); 51 | 52 | pv_circular = (ProgressView)v.findViewById(R.id.progress_pv_circular); 53 | pv_circular_colors = (ProgressView)v.findViewById(R.id.progress_pv_circular_colors); 54 | pv_circular_inout = (ProgressView)v.findViewById(R.id.progress_pv_circular_inout); 55 | pv_circular_inout_colors = (ProgressView)v.findViewById(R.id.progress_pv_circular_inout_colors); 56 | pv_circular_determinate_in_out = (ProgressView)v.findViewById(R.id.progress_pv_circular_determinate_in_out); 57 | pv_circular_determinate = (ProgressView)v.findViewById(R.id.progress_pv_circular_determinate); 58 | pv_linear = (ProgressView)v.findViewById(R.id.progress_pv_linear); 59 | pv_linear_colors = (ProgressView)v.findViewById(R.id.progress_pv_linear_colors); 60 | pv_linear_determinate = (ProgressView)v.findViewById(R.id.progress_pv_linear_determinate); 61 | pv_linear_query = (ProgressView)v.findViewById(R.id.progress_pv_linear_query); 62 | pv_linear_buffer = (ProgressView)v.findViewById(R.id.progress_pv_linear_buffer); 63 | 64 | mHandler = new Handler(this); 65 | 66 | return v; 67 | } 68 | 69 | @Override 70 | public void onPause() { 71 | super.onPause(); 72 | 73 | mHandler.removeCallbacksAndMessages(null); 74 | pv_circular_determinate_in_out.setVisibility(View.GONE); 75 | } 76 | 77 | @Override 78 | public void onResume() { 79 | super.onResume(); 80 | mHandler.sendEmptyMessageDelayed(MSG_START_PROGRESS, START_DELAY); 81 | } 82 | 83 | @Override 84 | public boolean handleMessage(Message msg) { 85 | switch (msg.what) { 86 | case MSG_START_PROGRESS: 87 | pv_circular.start(); 88 | pv_circular_colors.start(); 89 | pv_circular_inout.start(); 90 | pv_circular_inout_colors.start(); 91 | pv_circular_determinate_in_out.setProgress(0f); 92 | pv_circular_determinate_in_out.start(); 93 | pv_circular_determinate.setProgress(0f); 94 | pv_circular_determinate.start(); 95 | pv_linear.start(); 96 | pv_linear_colors.start(); 97 | pv_linear_determinate.setProgress(0f); 98 | pv_linear_determinate.start(); 99 | pv_linear_query.setProgress(0f); 100 | pv_linear_query.start(); 101 | pv_linear_buffer.setProgress(0f); 102 | pv_linear_buffer.setSecondaryProgress(0f); 103 | pv_linear_buffer.start(); 104 | mHandler.sendEmptyMessageDelayed(MSG_STOP_PROGRESS, PROGRESS_INTERVAL); 105 | mHandler.sendEmptyMessageDelayed(MSG_UPDATE_PROGRESS, PROGRESS_UPDATE_INTERVAL); 106 | mHandler.sendEmptyMessageDelayed(MSG_UPDATE_QUERY_PROGRESS, START_QUERY_DELAY); 107 | mHandler.sendEmptyMessageDelayed(MSG_UPDATE_BUFFER_PROGRESS, BUFFER_PROGRESS_UPDATE_INTERVAL); 108 | break; 109 | case MSG_UPDATE_QUERY_PROGRESS: 110 | pv_linear_query.setProgress(pv_linear_query.getProgress() + 0.01f); 111 | 112 | if(pv_linear_query.getProgress() < 1f) 113 | mHandler.sendEmptyMessageDelayed(MSG_UPDATE_QUERY_PROGRESS, QUERY_PROGRESS_UPDATE_INTERVAL); 114 | else 115 | pv_linear_query.stop(); 116 | break; 117 | case MSG_UPDATE_BUFFER_PROGRESS: 118 | pv_linear_buffer.setSecondaryProgress(pv_linear_buffer.getSecondaryProgress() + 0.01f); 119 | 120 | if(pv_linear_buffer.getSecondaryProgress() < 1f) 121 | mHandler.sendEmptyMessageDelayed(MSG_UPDATE_BUFFER_PROGRESS, BUFFER_PROGRESS_UPDATE_INTERVAL); 122 | break; 123 | case MSG_UPDATE_PROGRESS: 124 | pv_circular_determinate_in_out.setProgress(pv_circular_determinate_in_out.getProgress() + 0.01f); 125 | pv_circular_determinate.setProgress(pv_circular_determinate.getProgress() + 0.01f); 126 | 127 | pv_linear_determinate.setProgress(pv_linear_determinate.getProgress() + 0.01f); 128 | pv_linear_buffer.setProgress(pv_linear_buffer.getProgress() + 0.01f); 129 | if(pv_circular_determinate_in_out.getProgress() < 1f) 130 | mHandler.sendEmptyMessageDelayed(MSG_UPDATE_PROGRESS, PROGRESS_UPDATE_INTERVAL); 131 | else{ 132 | pv_circular_determinate_in_out.stop(); 133 | pv_circular_determinate.stop(); 134 | pv_linear_determinate.stop(); 135 | pv_linear_buffer.stop(); 136 | } 137 | break; 138 | case MSG_STOP_PROGRESS: 139 | pv_circular.stop(); 140 | pv_circular_colors.stop(); 141 | pv_circular_inout.stop(); 142 | pv_circular_inout_colors.stop(); 143 | pv_linear.stop(); 144 | pv_linear_colors.stop(); 145 | mHandler.sendEmptyMessageDelayed(MSG_START_PROGRESS, START_DELAY); 146 | break; 147 | } 148 | return false; 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /app/src/main/java/com/rey/material/demo/SliderFragment.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.demo; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import androidx.fragment.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | 12 | import com.rey.material.widget.Slider; 13 | 14 | public class SliderFragment extends Fragment{ 15 | 16 | 17 | public static SliderFragment newInstance(){ 18 | SliderFragment fragment = new SliderFragment(); 19 | 20 | return fragment; 21 | } 22 | 23 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 24 | @Override 25 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 26 | View v = inflater.inflate(R.layout.fragment_slider, container, false); 27 | 28 | Slider sl_continuous = (Slider)v.findViewById(R.id.slider_sl_continuous); 29 | final TextView tv_continuous = (TextView)v.findViewById(R.id.slider_tv_continuous); 30 | tv_continuous.setText(String.format("pos=%.1f value=%d", sl_continuous.getPosition(), sl_continuous.getValue())); 31 | sl_continuous.setOnPositionChangeListener(new Slider.OnPositionChangeListener() { 32 | @Override 33 | public void onPositionChanged(Slider view, boolean fromUser, float oldPos, float newPos, int oldValue, int newValue) { 34 | tv_continuous.setText(String.format("pos=%.1f value=%d", newPos, newValue)); 35 | } 36 | }); 37 | 38 | Slider sl_discrete = (Slider)v.findViewById(R.id.slider_sl_discrete); 39 | final TextView tv_discrete = (TextView)v.findViewById(R.id.slider_tv_discrete); 40 | tv_discrete.setText(String.format("pos=%.1f value=%d", sl_discrete.getPosition(), sl_discrete.getValue())); 41 | sl_discrete.setOnPositionChangeListener(new Slider.OnPositionChangeListener() { 42 | @Override 43 | public void onPositionChanged(Slider view, boolean fromUser, float oldPos, float newPos, int oldValue, int newValue) { 44 | tv_discrete.setText(String.format("pos=%.1f value=%d", newPos, newValue)); 45 | } 46 | }); 47 | 48 | return v; 49 | } 50 | 51 | @Override 52 | public void onPause() { 53 | super.onPause(); 54 | } 55 | 56 | @Override 57 | public void onResume() { 58 | super.onResume(); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/rey/material/demo/SnackbarFragment.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.demo; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import androidx.fragment.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import com.rey.material.widget.Button; 12 | import com.rey.material.widget.SnackBar; 13 | 14 | public class SnackbarFragment extends Fragment{ 15 | 16 | SnackBar mSnackBar; 17 | 18 | public static SnackbarFragment newInstance(){ 19 | SnackbarFragment fragment = new SnackbarFragment(); 20 | 21 | return fragment; 22 | } 23 | 24 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 25 | @Override 26 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 27 | View v = inflater.inflate(R.layout.fragment_snackbar, container, false); 28 | 29 | Button bt_mobile_single = (Button)v.findViewById(R.id.snackbar_bt_mobile_single); 30 | Button bt_mobile_multi = (Button)v.findViewById(R.id.snackbar_bt_mobile_multi); 31 | Button bt_tablet_single = (Button)v.findViewById(R.id.snackbar_bt_tablet_single); 32 | Button bt_tablet_multi = (Button)v.findViewById(R.id.snackbar_bt_tablet_multi); 33 | 34 | View.OnClickListener listener = new View.OnClickListener() { 35 | 36 | @Override 37 | public void onClick(View v) { 38 | if(mSnackBar.getState() == SnackBar.STATE_SHOWN) 39 | mSnackBar.dismiss(); 40 | else{ 41 | switch (v.getId()) { 42 | case R.id.snackbar_bt_mobile_single: 43 | mSnackBar.applyStyle(R.style.SnackBarSingleLine); 44 | mSnackBar.show(); 45 | break; 46 | case R.id.snackbar_bt_mobile_multi: 47 | mSnackBar.applyStyle(R.style.SnackBarMultiLine); 48 | mSnackBar.show(); 49 | break; 50 | case R.id.snackbar_bt_tablet_single: 51 | mSnackBar.applyStyle(R.style.Material_Widget_SnackBar_Tablet); 52 | mSnackBar.text("This is single-line snackbar.") 53 | .actionText("CLOSE") 54 | .duration(0) 55 | .show(); 56 | break; 57 | case R.id.snackbar_bt_tablet_multi: 58 | mSnackBar.applyStyle(R.style.Material_Widget_SnackBar_Tablet_MultiLine); 59 | mSnackBar.text("This is multi-line snackbar.\nIt will auto-close after 5s.") 60 | .actionText(null) 61 | .duration(5000) 62 | .show(); 63 | break; 64 | } 65 | } 66 | } 67 | }; 68 | 69 | 70 | bt_mobile_single.setOnClickListener(listener); 71 | bt_mobile_multi.setOnClickListener(listener); 72 | bt_tablet_single.setOnClickListener(listener); 73 | bt_tablet_multi.setOnClickListener(listener); 74 | 75 | mSnackBar = ((MainActivity)getActivity()).getSnackBar(); 76 | 77 | return v; 78 | } 79 | 80 | @Override 81 | public void onPause() { 82 | super.onPause(); 83 | } 84 | 85 | @Override 86 | public void onResume() { 87 | super.onResume(); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/rey/material/demo/SpinnersFragment.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.demo; 2 | 3 | import android.annotation.TargetApi; 4 | import android.graphics.drawable.Drawable; 5 | import android.os.Build; 6 | import android.os.Bundle; 7 | import androidx.fragment.app.Fragment; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.ArrayAdapter; 12 | 13 | import com.rey.material.widget.FloatingActionButton; 14 | import com.rey.material.widget.Spinner; 15 | 16 | public class SpinnersFragment extends Fragment{ 17 | 18 | 19 | public static SpinnersFragment newInstance(){ 20 | SpinnersFragment fragment = new SpinnersFragment(); 21 | 22 | return fragment; 23 | } 24 | 25 | private Drawable[] mDrawables = new Drawable[2]; 26 | private int index = 0; 27 | 28 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 29 | @Override 30 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 31 | View v = inflater.inflate(R.layout.fragment_spinner, container, false); 32 | 33 | Spinner spn_label = (Spinner)v.findViewById(R.id.spinner_label); 34 | Spinner spn_no_arrow = (Spinner)v.findViewById(R.id.spinner_no_arrow); 35 | String[] items = new String[20]; 36 | for(int i = 0; i < items.length; i++) 37 | items[i] = "Item " + String.valueOf(i + 1); 38 | ArrayAdapter adapter = new ArrayAdapter<>(getActivity(), R.layout.row_spn, items); 39 | adapter.setDropDownViewResource(R.layout.row_spn_dropdown); 40 | spn_label.setAdapter(adapter); 41 | spn_no_arrow.setAdapter(adapter); 42 | 43 | return v; 44 | } 45 | 46 | @Override 47 | public void onPause() { 48 | super.onPause(); 49 | } 50 | 51 | @Override 52 | public void onResume() { 53 | super.onResume(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/rey/material/demo/SwitchesFragment.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.demo; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import androidx.fragment.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.CompoundButton; 11 | 12 | import com.rey.material.widget.RadioButton; 13 | 14 | public class SwitchesFragment extends Fragment{ 15 | 16 | RadioButton rb1; 17 | RadioButton rb2; 18 | RadioButton rb3; 19 | 20 | public static SwitchesFragment newInstance(){ 21 | SwitchesFragment fragment = new SwitchesFragment(); 22 | 23 | return fragment; 24 | } 25 | 26 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 27 | @Override 28 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 29 | View v = inflater.inflate(R.layout.fragment_switches, container, false); 30 | 31 | rb1 = (RadioButton)v.findViewById(R.id.switches_rb1); 32 | rb2 = (RadioButton)v.findViewById(R.id.switches_rb2); 33 | rb3 = (RadioButton)v.findViewById(R.id.switches_rb3); 34 | 35 | CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() { 36 | 37 | @Override 38 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 39 | if(isChecked){ 40 | rb1.setChecked(rb1 == buttonView); 41 | rb2.setChecked(rb2 == buttonView); 42 | rb3.setChecked(rb3 == buttonView); 43 | } 44 | 45 | } 46 | 47 | }; 48 | 49 | rb1.setOnCheckedChangeListener(listener); 50 | rb2.setOnCheckedChangeListener(listener); 51 | rb3.setOnCheckedChangeListener(listener); 52 | 53 | return v; 54 | } 55 | 56 | @Override 57 | public void onPause() { 58 | super.onPause(); 59 | } 60 | 61 | @Override 62 | public void onResume() { 63 | super.onResume(); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/rey/material/demo/TextfieldFragment.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.demo; 2 | 3 | import com.rey.material.app.ContactEditText; 4 | import com.rey.material.drawable.ContactChipDrawable; 5 | import com.rey.material.text.style.ContactChipSpan; 6 | import com.rey.material.util.ThemeUtil; 7 | import com.rey.material.widget.EditText; 8 | 9 | import android.content.Context; 10 | import android.database.Cursor; 11 | import android.graphics.Bitmap; 12 | import android.graphics.BitmapFactory; 13 | import android.graphics.Typeface; 14 | import android.os.Build; 15 | import android.os.Bundle; 16 | import android.provider.ContactsContract; 17 | import androidx.fragment.app.Fragment; 18 | import android.text.SpannableStringBuilder; 19 | import android.text.Spanned; 20 | import android.view.KeyEvent; 21 | import android.view.LayoutInflater; 22 | import android.view.View; 23 | import android.view.ViewGroup; 24 | import android.widget.BaseAdapter; 25 | import android.widget.Filter; 26 | import android.widget.Filterable; 27 | import android.widget.TextView; 28 | 29 | import java.util.ArrayList; 30 | 31 | import static android.provider.ContactsContract.CommonDataKinds.Phone; 32 | 33 | public class TextfieldFragment extends Fragment{ 34 | 35 | public static TextfieldFragment newInstance(){ 36 | TextfieldFragment fragment = new TextfieldFragment(); 37 | 38 | return fragment; 39 | } 40 | 41 | @Override 42 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 43 | View v = inflater.inflate(R.layout.fragment_textfield, container, false); 44 | 45 | final EditText et_helper = (EditText)v.findViewById(R.id.textfield_et_helper); 46 | 47 | et_helper.setOnKeyListener(new View.OnKeyListener() { 48 | 49 | @Override 50 | public boolean onKey(View v, int keyCode, KeyEvent event) { 51 | if(keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) 52 | et_helper.setError("Password is incorrect."); 53 | 54 | return false; 55 | } 56 | 57 | }); 58 | 59 | et_helper.setOnFocusChangeListener(new View.OnFocusChangeListener() { 60 | 61 | @Override 62 | public void onFocusChange(View v, boolean hasFocus) { 63 | if(hasFocus) 64 | et_helper.setError(null); 65 | } 66 | 67 | }); 68 | 69 | final EditText et_helper_error = (EditText)v.findViewById(R.id.textfield_et_helper_error); 70 | 71 | et_helper_error.setOnKeyListener(new View.OnKeyListener() { 72 | 73 | @Override 74 | public boolean onKey(View v, int keyCode, KeyEvent event) { 75 | if(keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) 76 | et_helper_error.setError("Password is incorrect."); 77 | 78 | return false; 79 | } 80 | 81 | }); 82 | 83 | et_helper_error.setOnFocusChangeListener(new View.OnFocusChangeListener() { 84 | 85 | @Override 86 | public void onFocusChange(View v, boolean hasFocus) { 87 | if(hasFocus) 88 | et_helper_error.setError(null); 89 | } 90 | 91 | }); 92 | 93 | ContactEditText a = (ContactEditText) v.findViewById(R.id.textfield_tv); 94 | 95 | return v; 96 | } 97 | 98 | @Override 99 | public void onPause() { 100 | super.onPause(); 101 | } 102 | 103 | @Override 104 | public void onResume() { 105 | super.onResume(); 106 | } 107 | 108 | 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_scale_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_scale_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg_bt_raise.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/app/src/main/res/drawable-xhdpi/bg_bt_raise.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg_bt_raise_color.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/app/src/main/res/drawable-xhdpi/bg_bt_raise_color.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg_bt_raise_dark.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/app/src/main/res/drawable-xhdpi/bg_bt_raise_dark.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg_toolbar_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg_toolbar_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_autorenew_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/app/src/main/res/drawable-xhdpi/ic_autorenew_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_cancel_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/app/src/main/res/drawable-xhdpi/ic_cancel_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_done_all_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/app/src/main/res/drawable-xhdpi/ic_done_all_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_done_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/app/src/main/res/drawable-xhdpi/ic_done_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/app/src/main/res/drawable-xhdpi/ic_user.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_visibility_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/app/src/main/res/drawable-xhdpi/ic_visibility_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/popup_background_dark.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/app/src/main/res/drawable-xhdpi/popup_background_dark.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_window_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_window_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_divider_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_divider_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_label_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_label_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_switch_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_thumb_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_thumb_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_tpi_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_tpi_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_track_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_track_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_wv_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 17 | 18 | 24 | 25 | 32 | 33 | 37 | 38 | 39 | 40 | 46 | 47 | 48 | 49 | 55 | 56 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_fab.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 21 | 22 | 28 | 29 | 34 | 35 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_slider.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 21 | 22 | 30 | 31 | 37 | 38 | 46 | 47 | 52 | 53 | 61 | 62 | 68 | 69 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_snackbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 21 | 22 | 31 | 32 | 42 | 43 | 48 | 49 | 58 | 59 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_spinner.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 25 | 26 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_switches.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 21 | 22 | 30 | 31 | 39 | 40 | 48 | 49 | 54 | 55 | 63 | 64 | 72 | 73 | 81 | 82 | 87 | 88 | 96 | 97 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_textfield.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 29 | 30 | 39 | 40 | 51 | 52 | 63 | 64 | 75 | 76 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_dialog_custom.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 29 | 30 | 40 | 41 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/layout/rd_item_dropdown_end.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/rd_item_dropdown_mode.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/rd_item_end.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/rd_item_mode.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/row_contact_replace.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/row_contact_selected.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/row_drawer.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/row_spn.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/row_spn_dropdown.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_bottomsheet.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 22 | 23 | 31 | 32 | 36 | 37 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | 19 | 20 | 25 | 26 | 31 | 32 | 33 | 34 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #FF03A9F4 5 | #ff0171c9 6 | #FF00E5FF 7 | #FF78909C 8 | #FF03A9F4 9 | #FF78909C 10 | 11 | #3303A9F4 12 | #9003A9F4 13 | #9078909C 14 | #9003A9F4 15 | #3300E5FF 16 | #9000E5FF 17 | 18 | 19 | #FF03A9F4 20 | #FFD8433C 21 | #FFF2AF3A 22 | #FF279B5E 23 | 24 | 25 | 26 | #FF00E5FF 27 | #ffd81d5a 28 | #fff27512 29 | #ff63bc34 30 | 31 | 32 | 33 | #4B03A9F4 34 | #3303A9F4 35 | #1903A9F4 36 | 37 | 38 | 39 | #4B00E5FF 40 | #3300E5FF 41 | #1900E5FF 42 | 43 | 44 | #FFFFFFFF 45 | #99FFFFFF 46 | 47 | @color/colorControlActivated 48 | #FF6D6D6D 49 | #FFC1C1C1 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 240dp 4 | 20sp 5 | 16sp 6 | 7 | 36dp 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | MaterialDemo 4 | 5 | Repeat 6 | Daily 7 | Weekly 8 | Monthly 9 | Yearly 10 | None 11 | Every 12 | day 13 | days 14 | week 15 | weeks 16 | month 17 | months 18 | year 19 | years 20 | Forever 21 | Until 22 | Until a date 23 | For 24 | For a number of events 25 | on the same day each month 26 | on every last %s 27 | on every first %s 28 | on every second %s 29 | on every third %s 30 | on every fourth %s 31 | event 32 | events 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 22 | 23 | 27 | 28 | 32 | 33 | 34 | 35 | 40 | 41 | 46 | 47 | 51 | 52 | 56 | 57 | 58 | 68 | 69 | 82 | 83 | 98 | 99 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /app/src/main/res/xml/fab_icon_states.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 0.5 7 | 0 8 | 0.5 9 | 1 10 | 11 | 0 12 | 0.5 13 | 1 14 | 0.5 15 | 16 | 17 | 18 | 19 | 20 | 0.633 21 | 0.161 22 | 0 23 | 0.793 24 | 25 | 0.633 26 | 0.161 27 | 1 28 | 0.527 29 | 30 | 31 | 32 | 0 33 | 1 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | maven { 8 | url 'https://maven.google.com' 9 | } 10 | } 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:3.4.1' 13 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' 14 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' 15 | // NOTE: Do not place your application dependencies here; they belong 16 | // in the individual module build.gradle files 17 | } 18 | } 19 | 20 | allprojects { 21 | repositories { 22 | google() 23 | jcenter() 24 | } 25 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | -------------------------------------------------------------------------------- /gradle/local.properties: -------------------------------------------------------------------------------- 1 | ## This file is automatically generated by Android Studio. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must *NOT* be checked into Version Control Systems, 5 | # as it contains information specific to your local configuration. 6 | # 7 | # Location of the SDK. This is only used by Gradle. 8 | # For customization when using a Version Control System, please read the 9 | # header note. 10 | #Tue Dec 09 22:34:29 ICT 2014 11 | sdk.dir=C\:\\ADT\\sdk 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jun 12 11:01:23 ICT 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /image/bottomsheet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/bottomsheet.gif -------------------------------------------------------------------------------- /image/button_flat_touch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/button_flat_touch.gif -------------------------------------------------------------------------------- /image/button_flat_wave.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/button_flat_wave.gif -------------------------------------------------------------------------------- /image/button_raise_touch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/button_raise_touch.gif -------------------------------------------------------------------------------- /image/button_raise_wave.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/button_raise_wave.gif -------------------------------------------------------------------------------- /image/cb.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/cb.gif -------------------------------------------------------------------------------- /image/dialog_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/dialog_1.png -------------------------------------------------------------------------------- /image/dialog_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/dialog_2.png -------------------------------------------------------------------------------- /image/dialog_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/dialog_3.png -------------------------------------------------------------------------------- /image/dialog_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/dialog_4.png -------------------------------------------------------------------------------- /image/fab_image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/fab_image.gif -------------------------------------------------------------------------------- /image/fab_line.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/fab_line.gif -------------------------------------------------------------------------------- /image/progress_circular_determinate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/progress_circular_determinate.gif -------------------------------------------------------------------------------- /image/progress_circular_indeterminate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/progress_circular_indeterminate.gif -------------------------------------------------------------------------------- /image/progress_linear_buffer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/progress_linear_buffer.gif -------------------------------------------------------------------------------- /image/progress_linear_determinate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/progress_linear_determinate.gif -------------------------------------------------------------------------------- /image/progress_linear_indeterminate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/progress_linear_indeterminate.gif -------------------------------------------------------------------------------- /image/progress_linear_query.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/progress_linear_query.gif -------------------------------------------------------------------------------- /image/rb.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/rb.gif -------------------------------------------------------------------------------- /image/slider_continuous.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/slider_continuous.gif -------------------------------------------------------------------------------- /image/slider_discrete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/slider_discrete.gif -------------------------------------------------------------------------------- /image/snackbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/snackbar.png -------------------------------------------------------------------------------- /image/spn.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/spn.gif -------------------------------------------------------------------------------- /image/switch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/switch.gif -------------------------------------------------------------------------------- /image/textfield.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/textfield.gif -------------------------------------------------------------------------------- /image/theme.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/theme.gif -------------------------------------------------------------------------------- /image/tpi.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rey5137/material/b06373bf1dca194811c6e5374f5380cf04cfc859/image/tpi.gif -------------------------------------------------------------------------------- /material/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /material/bintray.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.jfrog.bintray' 2 | 3 | ext { 4 | bintrayUser = project.hasProperty('BINTRAY_USERNAME') ? BINTRAY_USERNAME : "" 5 | bintrayApiKey = project.hasProperty('BINTRAY_APIKEY') ? BINTRAY_APIKEY : "" 6 | bintrayGpgPassword = project.hasProperty('GPG_PASSWORD') ? GPG_PASSWORD : "" 7 | 8 | bintrayRepo = "maven" 9 | bintrayName = "material" 10 | 11 | libraryVersion = "1.3.1" 12 | libraryDescription = "An Android library to bring Material Design UI to pre-Lolipop Android." 13 | 14 | siteUrl = "https://github.com/rey5137/material" 15 | gitUrl = "https://github.com/rey5137/material.git" 16 | 17 | } 18 | 19 | version = libraryVersion 20 | 21 | task sourcesJar(type: Jar) { 22 | from android.sourceSets.main.java.srcDirs 23 | classifier = 'sources' 24 | } 25 | 26 | task javadoc(type: Javadoc) { 27 | source = android.sourceSets.main.java.srcDirs 28 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 29 | } 30 | 31 | task javadocJar(type: Jar, dependsOn: javadoc) { 32 | classifier = 'javadoc' 33 | from javadoc.destinationDir 34 | } 35 | 36 | artifacts { 37 | archives javadocJar 38 | archives sourcesJar 39 | } 40 | 41 | bintray { 42 | user = bintrayUser 43 | key = bintrayApiKey 44 | 45 | configurations = ['archives'] 46 | pkg { 47 | repo = bintrayRepo 48 | name = bintrayName 49 | desc = libraryDescription 50 | websiteUrl = siteUrl 51 | vcsUrl = gitUrl 52 | licenses = ["Apache-2.0"] 53 | publish = true 54 | publicDownloadNumbers = true 55 | version { 56 | desc = libraryDescription 57 | gpg { 58 | sign = true //Determines whether to GPG sign the files. The default is false 59 | passphrase = bintrayGpgPassword 60 | //Optional. The passphrase for GPG signing' 61 | } 62 | } 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /material/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion "28.0.3" 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion 28 10 | versionCode 22 11 | versionName "1.3.1" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | ext{ 22 | androidXVersion = '1.0.0' 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | testImplementation 'junit:junit:4.12' 28 | implementation "androidx.appcompat:appcompat:${androidXVersion}" 29 | implementation "androidx.cardview:cardview:${androidXVersion}" 30 | implementation "androidx.recyclerview:recyclerview:${androidXVersion}" 31 | } 32 | 33 | tasks.withType(Javadoc) { 34 | options.addStringOption('Xdoclint:none', '-quiet') 35 | options.addStringOption('encoding', 'UTF-8') 36 | options.addStringOption('charSet', 'UTF-8') 37 | } 38 | 39 | // Place it at the end of the file 40 | apply from: 'install.gradle' 41 | apply from: 'bintray.gradle' 42 | -------------------------------------------------------------------------------- /material/install.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.github.dcendents.android-maven' 2 | 3 | ext { 4 | publishedGroupId = "com.github.rey5137" 5 | 6 | artifact = "material" 7 | libraryName = "Material Library" 8 | libraryDescription = "An Android library to bring Material Design UI to pre-Lolipop Android." 9 | siteUrl = "https://github.com/rey5137/material" 10 | 11 | developerId = "rey5137" 12 | developerName = "Rey Pham" 13 | developerEmail = "pea5137@gmail.com" 14 | } 15 | 16 | group = publishedGroupId 17 | 18 | install { 19 | repositories.mavenInstaller { 20 | pom { 21 | project { 22 | packaging 'aar' 23 | groupId publishedGroupId 24 | artifactId artifact 25 | name libraryName 26 | description libraryDescription 27 | url siteUrl 28 | 29 | licenses { 30 | license { 31 | name 'The Apache Software License, Version 2.0' 32 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 33 | } 34 | } 35 | 36 | developers { 37 | developer { 38 | id developerId 39 | name developerName 40 | email developerEmail 41 | } 42 | } 43 | 44 | scm { 45 | connection siteUrl 46 | developerConnection siteUrl 47 | url siteUrl 48 | } 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /material/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\vietph\android\adt-bundle-windows-x86_64-20140702\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /material/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/app/DialogFragment.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.app; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.os.Bundle; 6 | import android.os.Parcelable; 7 | import androidx.annotation.NonNull; 8 | import android.view.View; 9 | 10 | /** 11 | * Created by Rey on 1/12/2015. 12 | */ 13 | public class DialogFragment extends androidx.fragment.app.DialogFragment { 14 | 15 | /** 16 | * Interface definition for passing style data. 17 | */ 18 | public interface Builder{ 19 | /** 20 | * Get a Dialog instance used for this fragment. 21 | * @param context A Context instance. 22 | * @return The Dialog will be used for this fragment. 23 | */ 24 | Dialog build(Context context); 25 | 26 | /** 27 | * Handle click event on Positive Action. 28 | */ 29 | void onPositiveActionClicked(DialogFragment fragment); 30 | 31 | /** 32 | * Handle click event on Negative Action. 33 | */ 34 | void onNegativeActionClicked(DialogFragment fragment); 35 | 36 | /** 37 | * Handle click event on Neutral Action. 38 | */ 39 | void onNeutralActionClicked(DialogFragment fragment); 40 | 41 | /** 42 | * Handle OnCancel event of dialog. 43 | * @param dialog 44 | */ 45 | void onCancel(DialogInterface dialog); 46 | 47 | /** 48 | * Handle OnDismiss event of dialog. 49 | * @param dialog 50 | */ 51 | void onDismiss(DialogInterface dialog); 52 | } 53 | 54 | protected static final String ARG_BUILDER = "arg_builder"; 55 | 56 | protected Builder mBuilder; 57 | 58 | private View.OnClickListener mActionListener = new View.OnClickListener() { 59 | @Override 60 | public void onClick(View v) { 61 | if(mBuilder == null) 62 | return; 63 | 64 | if(v.getId() == Dialog.ACTION_POSITIVE) 65 | mBuilder.onPositiveActionClicked(DialogFragment.this); 66 | else if(v.getId() == Dialog.ACTION_NEGATIVE) 67 | mBuilder.onNegativeActionClicked(DialogFragment.this); 68 | else if(v.getId() == Dialog.ACTION_NEUTRAL) 69 | mBuilder.onNeutralActionClicked(DialogFragment.this); 70 | } 71 | }; 72 | 73 | public static DialogFragment newInstance(Builder builder){ 74 | DialogFragment fragment = new DialogFragment(); 75 | fragment.mBuilder = builder; 76 | return fragment; 77 | } 78 | 79 | @NonNull 80 | @Override 81 | public Dialog onCreateDialog(Bundle savedInstanceState) { 82 | Dialog dialog = mBuilder == null ? new Dialog(getActivity()) : mBuilder.build(getActivity()); 83 | dialog.positiveActionClickListener(mActionListener) 84 | .negativeActionClickListener(mActionListener) 85 | .neutralActionClickListener(mActionListener); 86 | return dialog; 87 | } 88 | 89 | @Override 90 | public void onCreate(Bundle savedInstanceState) { 91 | super.onCreate(savedInstanceState); 92 | if(savedInstanceState != null && mBuilder == null) 93 | mBuilder = savedInstanceState.getParcelable(ARG_BUILDER); 94 | } 95 | 96 | @Override 97 | public void onSaveInstanceState(Bundle outState) { 98 | super.onSaveInstanceState(outState); 99 | 100 | if(mBuilder != null && mBuilder instanceof Parcelable) 101 | outState.putParcelable(ARG_BUILDER, (Parcelable)mBuilder); 102 | } 103 | 104 | @Override 105 | public void onDestroyView() { 106 | android.app.Dialog dialog = getDialog(); 107 | 108 | if(dialog != null && dialog instanceof Dialog) 109 | ((Dialog)dialog).dismissImmediately(); 110 | 111 | super.onDestroyView(); 112 | } 113 | 114 | @Override 115 | public void onCancel(DialogInterface dialog) { 116 | super.onCancel(dialog); 117 | mBuilder.onCancel(dialog); 118 | } 119 | 120 | @Override 121 | public void onDismiss(DialogInterface dialog) { 122 | super.onDismiss(dialog); 123 | mBuilder.onDismiss(dialog); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/drawable/ArrowDrawable.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.drawable; 2 | 3 | import android.content.res.ColorStateList; 4 | import android.graphics.Canvas; 5 | import android.graphics.ColorFilter; 6 | import android.graphics.Paint; 7 | import android.graphics.Path; 8 | import android.graphics.PixelFormat; 9 | import android.graphics.Rect; 10 | import android.graphics.drawable.Animatable; 11 | import android.graphics.drawable.Drawable; 12 | import android.os.SystemClock; 13 | import android.view.animation.DecelerateInterpolator; 14 | import android.view.animation.Interpolator; 15 | 16 | import com.rey.material.util.ViewUtil; 17 | 18 | public class ArrowDrawable extends Drawable implements Animatable{ 19 | 20 | private boolean mRunning = false; 21 | private long mStartTime; 22 | private float mAnimProgress; 23 | private int mAnimDuration; 24 | 25 | private Paint mPaint; 26 | private ColorStateList mColorStateList; 27 | private int mSize; 28 | private int mCurColor; 29 | private int mMode; 30 | private Interpolator mInterpolator; 31 | 32 | private Path mPath; 33 | 34 | public static int MODE_DOWN = 0; 35 | public static int MODE_UP = 1; 36 | 37 | private boolean mClockwise = true; 38 | 39 | public ArrowDrawable(int mode, int size, ColorStateList colorStateList, int animDuration, Interpolator interpolator, boolean clockwise){ 40 | mSize = size; 41 | mAnimDuration = animDuration; 42 | mMode = mode; 43 | mInterpolator = interpolator; 44 | if(mInterpolator == null) 45 | mInterpolator = new DecelerateInterpolator(); 46 | mClockwise = clockwise; 47 | 48 | mPaint = new Paint(); 49 | mPaint.setAntiAlias(true); 50 | mPaint.setStyle(Paint.Style.FILL); 51 | 52 | mPath = new Path(); 53 | 54 | setColor(colorStateList); 55 | } 56 | 57 | public void setColor(ColorStateList colorStateList){ 58 | mColorStateList = colorStateList; 59 | onStateChange(getState()); 60 | } 61 | 62 | public void setAnimationDuration(int duration){ 63 | mAnimDuration = duration; 64 | } 65 | 66 | public void setInterpolator(Interpolator interpolator){ 67 | mInterpolator = interpolator; 68 | } 69 | 70 | public void setClockwise(boolean clockwise){ 71 | mClockwise = clockwise; 72 | } 73 | 74 | public void setArrowSize(int size){ 75 | if(mSize != size){ 76 | mSize = size; 77 | invalidateSelf(); 78 | } 79 | } 80 | 81 | public void setMode(int mode, boolean animation){ 82 | if(mMode != mode){ 83 | mMode = mode; 84 | if(animation && mAnimDuration > 0) 85 | start(); 86 | else 87 | invalidateSelf(); 88 | } 89 | } 90 | 91 | public int getMode(){ 92 | return mMode; 93 | } 94 | 95 | @Override 96 | protected void onBoundsChange(Rect bounds) { 97 | float x = bounds.exactCenterX(); 98 | float y = bounds.exactCenterY(); 99 | 100 | mPath.reset(); 101 | mPath.moveTo(x, y + mSize / 2f); 102 | mPath.lineTo(x - mSize, y - mSize / 2f); 103 | mPath.lineTo(x + mSize, y - mSize / 2f); 104 | mPath.close(); 105 | } 106 | 107 | @Override 108 | public void draw(Canvas canvas) { 109 | int saveCount = canvas.save(); 110 | Rect bounds = getBounds(); 111 | 112 | if(!isRunning()){ 113 | if(mMode == MODE_UP) 114 | canvas.rotate(180, bounds.exactCenterX(), bounds.exactCenterY()); 115 | } 116 | else{ 117 | float value = mInterpolator.getInterpolation(mAnimProgress); 118 | float degree; 119 | 120 | if(mClockwise){ 121 | if(mMode == MODE_UP) // move down > up 122 | degree = 180 * value; 123 | else // move up > down 124 | degree = 180 * (1 + value); 125 | } 126 | else{ 127 | if(mMode == MODE_UP) // move down > up 128 | degree = -180 * value; 129 | else // move up > down 130 | degree = -180 * (1 + value); 131 | } 132 | 133 | canvas.rotate(degree, bounds.exactCenterX(), bounds.exactCenterY()); 134 | } 135 | 136 | mPaint.setColor(mCurColor); 137 | canvas.drawPath(mPath, mPaint); 138 | 139 | canvas.restoreToCount(saveCount); 140 | } 141 | 142 | @Override 143 | public void setAlpha(int alpha) { 144 | mPaint.setAlpha(alpha); 145 | } 146 | 147 | @Override 148 | public void setColorFilter(ColorFilter cf) { 149 | mPaint.setColorFilter(cf); 150 | } 151 | 152 | @Override 153 | public int getOpacity() { 154 | return PixelFormat.TRANSLUCENT; 155 | } 156 | 157 | @Override 158 | public boolean isStateful() { 159 | return true; 160 | } 161 | 162 | @Override 163 | protected boolean onStateChange(int[] state) { 164 | int color = mColorStateList.getColorForState(state, mCurColor); 165 | 166 | if(mCurColor != color){ 167 | mCurColor = color; 168 | return true; 169 | } 170 | 171 | return false; 172 | } 173 | 174 | private void resetAnimation(){ 175 | mStartTime = SystemClock.uptimeMillis(); 176 | mAnimProgress = 0f; 177 | } 178 | 179 | @Override 180 | public void start() { 181 | resetAnimation(); 182 | scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); 183 | invalidateSelf(); 184 | } 185 | 186 | @Override 187 | public void stop() { 188 | mRunning = false; 189 | unscheduleSelf(mUpdater); 190 | invalidateSelf(); 191 | } 192 | 193 | @Override 194 | public boolean isRunning() { 195 | return mRunning; 196 | } 197 | 198 | @Override 199 | public void scheduleSelf(Runnable what, long when) { 200 | mRunning = true; 201 | super.scheduleSelf(what, when); 202 | } 203 | 204 | private final Runnable mUpdater = new Runnable() { 205 | 206 | @Override 207 | public void run() { 208 | update(); 209 | } 210 | 211 | }; 212 | 213 | private void update(){ 214 | long curTime = SystemClock.uptimeMillis(); 215 | mAnimProgress = Math.min(1f, (float)(curTime - mStartTime) / mAnimDuration); 216 | 217 | if(mAnimProgress == 1f) 218 | mRunning = false; 219 | 220 | if(isRunning()) 221 | scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); 222 | 223 | invalidateSelf(); 224 | } 225 | 226 | } 227 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/drawable/BlankDrawable.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.drawable; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.ColorFilter; 5 | import android.graphics.PixelFormat; 6 | import android.graphics.drawable.Drawable; 7 | 8 | /** 9 | * A drawable that draw nothing. 10 | * @author Rey 11 | * 12 | */ 13 | public class BlankDrawable extends Drawable { 14 | 15 | private static BlankDrawable mInstance; 16 | 17 | public static BlankDrawable getInstance(){ 18 | if(mInstance == null) 19 | synchronized (BlankDrawable.class) { 20 | if(mInstance == null) 21 | mInstance = new BlankDrawable(); 22 | } 23 | 24 | return mInstance; 25 | } 26 | 27 | @Override 28 | public void draw(Canvas canvas) {} 29 | 30 | @Override 31 | public void setAlpha(int alpha) {} 32 | 33 | @Override 34 | public void setColorFilter(ColorFilter cf) {} 35 | 36 | @Override 37 | public int getOpacity() { 38 | return PixelFormat.TRANSPARENT; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/drawable/CircleDrawable.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.drawable; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.ColorFilter; 5 | import android.graphics.Paint; 6 | import android.graphics.PixelFormat; 7 | import android.graphics.Rect; 8 | import android.graphics.drawable.Animatable; 9 | import android.graphics.drawable.Drawable; 10 | import android.os.SystemClock; 11 | import android.view.animation.DecelerateInterpolator; 12 | import android.view.animation.Interpolator; 13 | 14 | import com.rey.material.util.ViewUtil; 15 | 16 | /** 17 | * Created by Rey on 12/26/2014. 18 | */ 19 | public class CircleDrawable extends Drawable implements Animatable { 20 | 21 | private boolean mRunning = false; 22 | private long mStartTime; 23 | private float mAnimProgress; 24 | private int mAnimDuration = 1000; 25 | private Interpolator mInInterpolator = new DecelerateInterpolator(); 26 | private Interpolator mOutInterpolator = new DecelerateInterpolator(); 27 | 28 | private Paint mPaint; 29 | 30 | private float mX; 31 | private float mY; 32 | private float mRadius; 33 | 34 | private boolean mVisible; 35 | private boolean mInEditMode = false; 36 | private boolean mAnimEnable = true; 37 | 38 | public CircleDrawable() { 39 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 40 | mPaint.setStyle(Paint.Style.FILL); 41 | } 42 | 43 | public void setInEditMode(boolean b) { 44 | mInEditMode = b; 45 | } 46 | 47 | public void setAnimEnable(boolean b) { 48 | mAnimEnable = b; 49 | } 50 | 51 | public void setColor(int color) { 52 | mPaint.setColor(color); 53 | invalidateSelf(); 54 | } 55 | 56 | public void setAnimDuration(int duration) { 57 | mAnimDuration = duration; 58 | } 59 | 60 | public void setInterpolator(Interpolator in, Interpolator out) { 61 | mInInterpolator = in; 62 | mOutInterpolator = out; 63 | } 64 | 65 | @Override 66 | public boolean isStateful() { 67 | return true; 68 | } 69 | 70 | @Override 71 | protected boolean onStateChange(int[] state) { 72 | boolean visible = ViewUtil.hasState(state, android.R.attr.state_checked) || ViewUtil.hasState(state, android.R.attr.state_pressed); 73 | 74 | if (mVisible != visible) { 75 | mVisible = visible; 76 | if (!mInEditMode && mAnimEnable) 77 | start(); 78 | return true; 79 | } 80 | 81 | return false; 82 | } 83 | 84 | @Override 85 | protected void onBoundsChange(Rect bounds) { 86 | mX = bounds.exactCenterX(); 87 | mY = bounds.exactCenterY(); 88 | mRadius = Math.min(bounds.width(), bounds.height()) / 2f; 89 | } 90 | 91 | @Override 92 | public void draw(Canvas canvas) { 93 | if (!mRunning) { 94 | if (mVisible) 95 | canvas.drawCircle(mX, mY, mRadius, mPaint); 96 | } else { 97 | float radius = mVisible ? mInInterpolator.getInterpolation(mAnimProgress) * mRadius : (1f - mOutInterpolator.getInterpolation(mAnimProgress)) * mRadius; 98 | canvas.drawCircle(mX, mY, radius, mPaint); 99 | } 100 | } 101 | 102 | @Override 103 | public void setAlpha(int alpha) { 104 | mPaint.setAlpha(alpha); 105 | } 106 | 107 | @Override 108 | public void setColorFilter(ColorFilter cf) { 109 | mPaint.setColorFilter(cf); 110 | } 111 | 112 | @Override 113 | public int getOpacity() { 114 | return PixelFormat.TRANSLUCENT; 115 | } 116 | 117 | private void resetAnimation() { 118 | mStartTime = SystemClock.uptimeMillis(); 119 | mAnimProgress = 0f; 120 | } 121 | 122 | @Override 123 | public void start() { 124 | resetAnimation(); 125 | scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); 126 | invalidateSelf(); 127 | } 128 | 129 | @Override 130 | public void stop() { 131 | mRunning = false; 132 | unscheduleSelf(mUpdater); 133 | invalidateSelf(); 134 | } 135 | 136 | @Override 137 | public boolean isRunning() { 138 | return mRunning; 139 | } 140 | 141 | @Override 142 | public void scheduleSelf(Runnable what, long when) { 143 | mRunning = true; 144 | super.scheduleSelf(what, when); 145 | } 146 | 147 | private final Runnable mUpdater = new Runnable() { 148 | 149 | @Override 150 | public void run() { 151 | update(); 152 | } 153 | 154 | }; 155 | 156 | private void update() { 157 | long curTime = SystemClock.uptimeMillis(); 158 | mAnimProgress = Math.min(1f, (float) (curTime - mStartTime) / mAnimDuration); 159 | 160 | if (mAnimProgress == 1f) 161 | mRunning = false; 162 | 163 | if (isRunning()) 164 | scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); 165 | 166 | invalidateSelf(); 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/drawable/ContactChipDrawable.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.drawable; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapShader; 5 | import android.graphics.Canvas; 6 | import android.graphics.ColorFilter; 7 | import android.graphics.Matrix; 8 | import android.graphics.Paint; 9 | import android.graphics.PixelFormat; 10 | import android.graphics.Rect; 11 | import android.graphics.RectF; 12 | import android.graphics.Shader; 13 | import android.graphics.Typeface; 14 | import android.graphics.drawable.Drawable; 15 | import android.text.BoringLayout; 16 | import android.text.Layout; 17 | import android.text.TextPaint; 18 | import android.text.TextUtils; 19 | 20 | /** 21 | * Created by Rey on 1/21/2015. 22 | */ 23 | public class ContactChipDrawable extends Drawable{ 24 | 25 | private Paint mPaint; 26 | private int mPaddingLeft; 27 | private int mPaddingRight; 28 | private int mBackgroundColor; 29 | 30 | private CharSequence mContactName; 31 | private BoringLayout mBoringLayout; 32 | private BoringLayout.Metrics mMetrics; 33 | private TextPaint mTextPaint; 34 | private RectF mRect; 35 | 36 | private BitmapShader mBitmapShader; 37 | private Bitmap mBitmap; 38 | private Matrix mMatrix; 39 | 40 | public ContactChipDrawable(int paddingLeft, int paddingRight, Typeface typeface, int textColor, int textSize, int backgroundColor) { 41 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 42 | mPaint.setStyle(Paint.Style.FILL); 43 | mPaint.setColor(textColor); 44 | mPaint.setTypeface(typeface); 45 | mPaint.setTextSize(textSize); 46 | 47 | mTextPaint = new TextPaint(mPaint); 48 | mMetrics = new BoringLayout.Metrics(); 49 | Paint.FontMetricsInt temp = mTextPaint.getFontMetricsInt(); 50 | mMetrics.ascent = temp.ascent; 51 | mMetrics.bottom = temp.bottom; 52 | mMetrics.descent = temp.descent; 53 | mMetrics.top = temp.top; 54 | mMetrics.leading = temp.leading; 55 | 56 | mRect = new RectF(); 57 | 58 | mMatrix = new Matrix(); 59 | 60 | mPaddingLeft = paddingLeft; 61 | mPaddingRight = paddingRight; 62 | mBackgroundColor = backgroundColor; 63 | } 64 | 65 | public void setContactName(CharSequence name){ 66 | mContactName = name; 67 | updateLayout(); 68 | invalidateSelf(); 69 | } 70 | 71 | public void setImage(Bitmap bm){ 72 | if(mBitmap != bm){ 73 | mBitmap = bm; 74 | if(mBitmap != null) { 75 | mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 76 | updateMatrix(); 77 | } 78 | invalidateSelf(); 79 | } 80 | } 81 | 82 | private void updateLayout(){ 83 | if(mContactName == null) 84 | return; 85 | 86 | Rect bounds = getBounds(); 87 | if(bounds.width() == 0 || bounds.height() == 0) 88 | return; 89 | 90 | int outerWidth = Math.max(0, bounds.width() - bounds.height() - mPaddingLeft - mPaddingRight); 91 | mMetrics.width = Math.round(mTextPaint.measureText(mContactName, 0, mContactName.length()) + 0.5f); 92 | 93 | if(mBoringLayout == null) 94 | mBoringLayout = BoringLayout.make(mContactName, mTextPaint, outerWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 1f, mMetrics, true, TextUtils.TruncateAt.END, outerWidth); 95 | else 96 | mBoringLayout = mBoringLayout.replaceOrMake(mContactName, mTextPaint, outerWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 1f, mMetrics, true, TextUtils.TruncateAt.END, outerWidth); 97 | } 98 | 99 | private void updateMatrix(){ 100 | if(mBitmap == null) 101 | return; 102 | 103 | Rect bounds = getBounds(); 104 | if(bounds.width() == 0 || bounds.height() == 0) 105 | return; 106 | 107 | mMatrix.reset(); 108 | float scale = bounds.height() / (float)Math.min(mBitmap.getWidth(), mBitmap.getHeight()); 109 | mMatrix.setScale(scale, scale, 0, 0); 110 | mMatrix.postTranslate((bounds.height() - mBitmap.getWidth() * scale) / 2, (bounds.height() - mBitmap.getHeight() * scale) / 2); 111 | 112 | mBitmapShader.setLocalMatrix(mMatrix); 113 | } 114 | 115 | @Override 116 | protected void onBoundsChange(Rect bounds) { 117 | updateLayout(); 118 | updateMatrix(); 119 | } 120 | 121 | @Override 122 | public void draw(Canvas canvas) { 123 | int saveCount = canvas.save(); 124 | 125 | Rect bounds = getBounds(); 126 | float halfHeight = bounds.height() / 2f; 127 | mPaint.setShader(null); 128 | mPaint.setColor(mBackgroundColor); 129 | mRect.set(1, 0, bounds.height() + 1, bounds.height()); 130 | canvas.drawArc(mRect, 90, 180, true, mPaint); 131 | mRect.set(bounds.width() - bounds.height(), 0, bounds.width(), bounds.height()); 132 | canvas.drawArc(mRect, 270, 180, true, mPaint); 133 | mRect.set(halfHeight, 0, bounds.width() - halfHeight, bounds.height()); 134 | canvas.drawRect(mRect, mPaint); 135 | 136 | if(mBitmap != null){ 137 | mPaint.setShader(mBitmapShader); 138 | canvas.drawCircle(halfHeight, halfHeight, halfHeight, mPaint); 139 | } 140 | 141 | if(mContactName != null && mBoringLayout != null) { 142 | canvas.translate(bounds.height() + mPaddingLeft, (bounds.height() - mBoringLayout.getHeight()) / 2f); 143 | mBoringLayout.draw(canvas); 144 | } 145 | 146 | canvas.restoreToCount(saveCount); 147 | } 148 | 149 | @Override 150 | public void setAlpha(int alpha) { 151 | mPaint.setAlpha(alpha); 152 | } 153 | 154 | @Override 155 | public void setColorFilter(ColorFilter cf) { 156 | mPaint.setColorFilter(cf); 157 | } 158 | 159 | @Override 160 | public int getOpacity() { 161 | return PixelFormat.TRANSLUCENT; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/drawable/NavigationDrawerDrawable.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.drawable; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.ColorFilter; 7 | import android.graphics.PixelFormat; 8 | import android.graphics.drawable.Drawable; 9 | import android.util.AttributeSet; 10 | 11 | import com.rey.material.R; 12 | 13 | public class NavigationDrawerDrawable extends Drawable implements Drawable.Callback{ 14 | 15 | private ToolbarRippleDrawable mRippleDrawable; 16 | private LineMorphingDrawable mLineDrawable; 17 | 18 | public static final int STATE_DRAWER = 0; 19 | public static final int STATE_ARROW = 1; 20 | 21 | public NavigationDrawerDrawable(ToolbarRippleDrawable rippleDrawable, LineMorphingDrawable lineDrawable){ 22 | mRippleDrawable = rippleDrawable; 23 | mLineDrawable = lineDrawable; 24 | 25 | mRippleDrawable.setCallback(this); 26 | mLineDrawable.setCallback(this); 27 | } 28 | 29 | public void switchIconState(int state, boolean animation){ 30 | mLineDrawable.switchLineState(state, animation); 31 | } 32 | 33 | public int getIconState(){ 34 | return mLineDrawable.getLineState(); 35 | } 36 | 37 | public boolean setIconState(int state, float progress){ 38 | return mLineDrawable.setLineState(state, progress); 39 | } 40 | 41 | public float getIconAnimProgress(){ 42 | return mLineDrawable.getAnimProgress(); 43 | } 44 | 45 | public void cancel(){ 46 | if(mRippleDrawable != null) 47 | mRippleDrawable.cancel(); 48 | if(mLineDrawable != null) 49 | mLineDrawable.cancel(); 50 | } 51 | 52 | @Override 53 | public void draw(Canvas canvas) { 54 | mRippleDrawable.draw(canvas); 55 | mLineDrawable.draw(canvas); 56 | } 57 | 58 | @Override 59 | public void setAlpha(int alpha) { 60 | mRippleDrawable.setAlpha(alpha); 61 | mLineDrawable.setAlpha(alpha); 62 | } 63 | 64 | @Override 65 | public void setColorFilter(ColorFilter cf) { 66 | mRippleDrawable.setColorFilter(cf); 67 | mLineDrawable.setColorFilter(cf); 68 | } 69 | 70 | @Override 71 | public int getOpacity() { 72 | return PixelFormat.TRANSLUCENT; 73 | } 74 | 75 | @Override 76 | public void setBounds(int left, int top, int right, int bottom) { 77 | mRippleDrawable.setBounds(left, top, right, bottom); 78 | mLineDrawable.setBounds(left, top, right, bottom); 79 | } 80 | 81 | @Override 82 | public void setDither(boolean dither) { 83 | mRippleDrawable.setDither(dither); 84 | mLineDrawable.setDither(dither); 85 | } 86 | 87 | @Override 88 | public void invalidateDrawable(Drawable who) { 89 | invalidateSelf(); 90 | } 91 | 92 | @Override 93 | public void scheduleDrawable(Drawable who, Runnable what, long when) { 94 | scheduleSelf(what, when); 95 | } 96 | 97 | @Override 98 | public void unscheduleDrawable(Drawable who, Runnable what) { 99 | unscheduleSelf(what); 100 | } 101 | 102 | @Override 103 | public boolean isStateful() { 104 | return true; 105 | } 106 | 107 | @Override 108 | protected boolean onStateChange(int[] state) { 109 | return mRippleDrawable.onStateChange(state); 110 | } 111 | 112 | public static class Builder{ 113 | private ToolbarRippleDrawable mRippleDrawable; 114 | private LineMorphingDrawable mLineDrawable; 115 | 116 | public Builder(){} 117 | 118 | public Builder(Context context, int defStyleRes){ 119 | this(context, null, 0, defStyleRes); 120 | } 121 | 122 | public Builder(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 123 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NavigationDrawerDrawable, defStyleAttr, defStyleRes); 124 | 125 | if(a != null){ 126 | int rippleId = a.getResourceId(R.styleable.NavigationDrawerDrawable_nd_ripple, 0); 127 | int lineId = a.getResourceId(R.styleable.NavigationDrawerDrawable_nd_icon, 0); 128 | 129 | if(rippleId > 0) 130 | ripple(new ToolbarRippleDrawable.Builder(context, rippleId).build()); 131 | 132 | if(lineId > 0){ 133 | LineMorphingDrawable.Builder builder = new LineMorphingDrawable.Builder(context, lineId); 134 | line(builder.build()); 135 | } 136 | 137 | a.recycle(); 138 | } 139 | } 140 | 141 | public NavigationDrawerDrawable build(){ 142 | return new NavigationDrawerDrawable(mRippleDrawable, mLineDrawable); 143 | } 144 | 145 | public Builder ripple(ToolbarRippleDrawable drawable){ 146 | mRippleDrawable = drawable; 147 | 148 | return this; 149 | } 150 | 151 | public Builder line(LineMorphingDrawable drawable){ 152 | mLineDrawable = drawable; 153 | 154 | return this; 155 | } 156 | 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/drawable/ThemeDrawable.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.drawable; 2 | 3 | import android.graphics.drawable.Drawable; 4 | import android.graphics.drawable.LevelListDrawable; 5 | 6 | import com.rey.material.app.ThemeManager; 7 | 8 | /** 9 | * Created by Rey on 5/27/2015. 10 | */ 11 | public class ThemeDrawable extends LevelListDrawable implements ThemeManager.OnThemeChangedListener { 12 | private int mStyleId; 13 | 14 | public ThemeDrawable(int styleId) { 15 | mStyleId = styleId; 16 | 17 | if(mStyleId != 0) { 18 | ThemeManager.getInstance().registerOnThemeChangedListener(this); 19 | initDrawables(); 20 | } 21 | } 22 | 23 | private void initDrawables(){ 24 | ThemeManager themeManager = ThemeManager.getInstance(); 25 | int count = themeManager.getThemeCount(); 26 | 27 | for(int i = 0; i < count; i++){ 28 | Drawable drawable = themeManager.getContext().getResources().getDrawable(themeManager.getStyle(mStyleId, i)); 29 | addLevel(i, i, drawable); 30 | } 31 | 32 | setLevel(themeManager.getCurrentTheme()); 33 | } 34 | 35 | @Override 36 | public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) { 37 | if(getLevel() != event.theme) 38 | setLevel(event.theme); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/text/style/ContactChipSpan.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.text.style; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapShader; 5 | import android.graphics.Canvas; 6 | import android.graphics.Matrix; 7 | import android.graphics.Paint; 8 | import android.graphics.RectF; 9 | import android.graphics.Shader; 10 | import android.graphics.Typeface; 11 | import android.text.BoringLayout; 12 | import android.text.Layout; 13 | import android.text.TextPaint; 14 | import android.text.TextUtils; 15 | import android.text.style.ReplacementSpan; 16 | 17 | /** 18 | * Created by Rey on 1/21/2015. 19 | */ 20 | public class ContactChipSpan extends ReplacementSpan { 21 | 22 | private Paint mPaint; 23 | private int mPaddingLeft; 24 | private int mPaddingRight; 25 | private int mBackgroundColor; 26 | private int mHeight; 27 | private int mWidth; 28 | 29 | private CharSequence mContactName; 30 | private BoringLayout mBoringLayout; 31 | private TextPaint mTextPaint; 32 | private RectF mRect; 33 | 34 | private BitmapShader mBitmapShader; 35 | private Bitmap mBitmap; 36 | private Matrix mMatrix; 37 | 38 | public ContactChipSpan(CharSequence name, int height, int maxWidth, int paddingLeft, int paddingRight, Typeface typeface, int textColor, int textSize, int backgroundColor) { 39 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 40 | mPaint.setStyle(Paint.Style.FILL); 41 | mPaint.setColor(textColor); 42 | mPaint.setTypeface(typeface); 43 | mPaint.setTextSize(textSize); 44 | 45 | mTextPaint = new TextPaint(mPaint); 46 | 47 | 48 | mRect = new RectF(); 49 | 50 | mMatrix = new Matrix(); 51 | 52 | mContactName = name; 53 | mPaddingLeft = paddingLeft; 54 | mPaddingRight = paddingRight; 55 | mBackgroundColor = backgroundColor; 56 | mHeight = height; 57 | mWidth = Math.round(Math.min(maxWidth, mPaint.measureText(name, 0, name.length()) + paddingLeft + paddingRight + height)); 58 | 59 | int outerWidth = Math.max(0, mWidth - mPaddingLeft - mPaddingRight - mHeight); 60 | Paint.FontMetricsInt temp = mTextPaint.getFontMetricsInt(); 61 | BoringLayout.Metrics mMetrics = new BoringLayout.Metrics(); 62 | mMetrics.width = Math.round(mTextPaint.measureText(mContactName, 0, mContactName.length()) + 0.5f); 63 | mMetrics.ascent = temp.ascent; 64 | mMetrics.bottom = temp.bottom; 65 | mMetrics.descent = temp.descent; 66 | mMetrics.top = temp.top; 67 | mMetrics.leading = temp.leading; 68 | mBoringLayout = BoringLayout.make(mContactName, mTextPaint, outerWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 1f, mMetrics, true, TextUtils.TruncateAt.END, outerWidth); 69 | } 70 | 71 | public void setImage(Bitmap bm){ 72 | if(mBitmap != bm){ 73 | mBitmap = bm; 74 | if(mBitmap != null) { 75 | mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 76 | mMatrix.reset(); 77 | float scale = mHeight / (float)Math.min(mBitmap.getWidth(), mBitmap.getHeight()); 78 | mMatrix.setScale(scale, scale, 0, 0); 79 | mMatrix.postTranslate((mHeight - mBitmap.getWidth() * scale) / 2, (mHeight - mBitmap.getHeight() * scale) / 2); 80 | 81 | mBitmapShader.setLocalMatrix(mMatrix); 82 | } 83 | } 84 | } 85 | 86 | @Override 87 | public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { 88 | if (fm != null) { 89 | int cy = (fm.ascent + fm.descent) / 2; 90 | fm.ascent = Math.min(fm.ascent, cy - mHeight / 2); 91 | fm.descent = Math.max(fm.descent, cy + mHeight / 2); 92 | fm.top = Math.min(fm.top, fm.ascent); 93 | fm.bottom = Math.max(fm.bottom, fm.descent); 94 | } 95 | 96 | return mWidth; 97 | } 98 | 99 | @Override 100 | public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { 101 | canvas.save(); 102 | 103 | canvas.translate(x, top); 104 | 105 | float halfHeight = mHeight / 2f; 106 | mPaint.setShader(null); 107 | mPaint.setColor(mBackgroundColor); 108 | mRect.set(1, 0, mHeight + 1, mHeight); 109 | canvas.drawArc(mRect, 90, 180, true, mPaint); 110 | mRect.set(mWidth - mHeight, 0, mWidth, mHeight); 111 | canvas.drawArc(mRect, 270, 180, true, mPaint); 112 | mRect.set(halfHeight, 0, mWidth - halfHeight, mHeight); 113 | canvas.drawRect(mRect, mPaint); 114 | 115 | if(mBitmap != null){ 116 | mPaint.setShader(mBitmapShader); 117 | canvas.drawCircle(halfHeight, halfHeight, halfHeight, mPaint); 118 | } 119 | 120 | if(mContactName != null && mBoringLayout != null) { 121 | canvas.translate(mHeight + mPaddingLeft, (mHeight - mBoringLayout.getHeight()) / 2f); 122 | mBoringLayout.draw(canvas); 123 | } 124 | 125 | canvas.restore(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/util/ColorUtil.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.util; 2 | 3 | import android.graphics.Color; 4 | 5 | public class ColorUtil { 6 | 7 | private static int getMiddleValue(int prev, int next, float factor){ 8 | return Math.round(prev + (next - prev) * factor); 9 | } 10 | 11 | public static int getMiddleColor(int prevColor, int curColor, float factor){ 12 | if(prevColor == curColor) 13 | return curColor; 14 | 15 | if(factor == 0f) 16 | return prevColor; 17 | else if(factor == 1f) 18 | return curColor; 19 | 20 | int a = getMiddleValue(Color.alpha(prevColor), Color.alpha(curColor), factor); 21 | int r = getMiddleValue(Color.red(prevColor), Color.red(curColor), factor); 22 | int g = getMiddleValue(Color.green(prevColor), Color.green(curColor), factor); 23 | int b = getMiddleValue(Color.blue(prevColor), Color.blue(curColor), factor); 24 | 25 | return Color.argb(a, r, g, b); 26 | } 27 | 28 | public static int getColor(int baseColor, float alphaPercent){ 29 | int alpha = Math.round(Color.alpha(baseColor) * alphaPercent); 30 | 31 | return (baseColor & 0x00FFFFFF) | (alpha << 24); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/util/ThemeUtil.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.util; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.content.res.Resources.Theme; 6 | import android.content.res.TypedArray; 7 | import android.os.Build; 8 | import android.util.TypedValue; 9 | 10 | import com.rey.material.R; 11 | 12 | public class ThemeUtil { 13 | 14 | private static TypedValue value; 15 | 16 | public static int dpToPx(Context context, int dp){ 17 | return (int)(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics()) + 0.5f); 18 | } 19 | 20 | public static int spToPx(Context context, int sp){ 21 | return (int)(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, context.getResources().getDisplayMetrics()) + 0.5f); 22 | } 23 | 24 | private static int getColor(Context context, int id, int defaultValue){ 25 | if(value == null) 26 | value = new TypedValue(); 27 | 28 | try{ 29 | Theme theme = context.getTheme(); 30 | if(theme != null && theme.resolveAttribute(id, value, true)){ 31 | if (value.type >= TypedValue.TYPE_FIRST_INT && value.type <= TypedValue.TYPE_LAST_INT) 32 | return value.data; 33 | else if (value.type == TypedValue.TYPE_STRING) 34 | return context.getResources().getColor(value.resourceId); 35 | } 36 | } 37 | catch(Exception ex){} 38 | 39 | return defaultValue; 40 | } 41 | 42 | public static int windowBackground(Context context, int defaultValue){ 43 | return getColor(context, android.R.attr.windowBackground, defaultValue); 44 | } 45 | 46 | public static int textColorPrimary(Context context, int defaultValue){ 47 | return getColor(context, android.R.attr.textColorPrimary, defaultValue); 48 | } 49 | 50 | public static int textColorSecondary(Context context, int defaultValue){ 51 | return getColor(context, android.R.attr.textColorSecondary, defaultValue); 52 | } 53 | 54 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 55 | public static int colorPrimary(Context context, int defaultValue){ 56 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 57 | return getColor(context, android.R.attr.colorPrimary, defaultValue); 58 | 59 | return getColor(context, R.attr.colorPrimary, defaultValue); 60 | } 61 | 62 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 63 | public static int colorPrimaryDark(Context context, int defaultValue){ 64 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 65 | return getColor(context, android.R.attr.colorPrimaryDark, defaultValue); 66 | 67 | return getColor(context, R.attr.colorPrimaryDark, defaultValue); 68 | } 69 | 70 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 71 | public static int colorAccent(Context context, int defaultValue){ 72 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 73 | return getColor(context, android.R.attr.colorAccent, defaultValue); 74 | 75 | return getColor(context, R.attr.colorAccent, defaultValue); 76 | } 77 | 78 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 79 | public static int colorControlNormal(Context context, int defaultValue){ 80 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 81 | return getColor(context, android.R.attr.colorControlNormal, defaultValue); 82 | 83 | return getColor(context, R.attr.colorControlNormal, defaultValue); 84 | } 85 | 86 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 87 | public static int colorControlActivated(Context context, int defaultValue){ 88 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 89 | return getColor(context, android.R.attr.colorControlActivated, defaultValue); 90 | 91 | return getColor(context, R.attr.colorControlActivated, defaultValue); 92 | } 93 | 94 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 95 | public static int colorControlHighlight(Context context, int defaultValue){ 96 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 97 | return getColor(context, android.R.attr.colorControlHighlight, defaultValue); 98 | 99 | return getColor(context, R.attr.colorControlHighlight, defaultValue); 100 | } 101 | 102 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 103 | public static int colorButtonNormal(Context context, int defaultValue){ 104 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 105 | return getColor(context, android.R.attr.colorButtonNormal, defaultValue); 106 | 107 | return getColor(context, R.attr.colorButtonNormal, defaultValue); 108 | } 109 | 110 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 111 | public static int colorSwitchThumbNormal(Context context, int defaultValue){ 112 | return getColor(context, R.attr.colorSwitchThumbNormal, defaultValue); 113 | } 114 | 115 | public static int getType(TypedArray array, int index){ 116 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 117 | return array.getType(index); 118 | else{ 119 | TypedValue value = array.peekValue(index); 120 | return value == null ? TypedValue.TYPE_NULL : value.type; 121 | } 122 | } 123 | 124 | public static CharSequence getString(TypedArray array, int index, CharSequence defaultValue){ 125 | String result = array.getString(index); 126 | return result == null ? defaultValue : result; 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/util/TypefaceUtil.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.util; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | 6 | import java.util.HashMap; 7 | 8 | /** 9 | * Created by Rey on 12/23/2014. 10 | */ 11 | public class TypefaceUtil { 12 | 13 | private static final HashMap sCachedFonts = new HashMap(); 14 | private static final String PREFIX_ASSET = "asset:"; 15 | 16 | private TypefaceUtil() { 17 | } 18 | 19 | /** 20 | * @param familyName if start with 'asset:' prefix, then load font from asset folder. 21 | * @return 22 | */ 23 | public static Typeface load(Context context, String familyName, int style) { 24 | if(familyName != null && familyName.startsWith(PREFIX_ASSET)) 25 | synchronized (sCachedFonts) { 26 | try { 27 | if (!sCachedFonts.containsKey(familyName)) { 28 | final Typeface typeface = Typeface.createFromAsset(context.getAssets(), familyName.substring(PREFIX_ASSET.length())); 29 | sCachedFonts.put(familyName, typeface); 30 | return typeface; 31 | } 32 | } catch (Exception e) { 33 | return Typeface.DEFAULT; 34 | } 35 | 36 | return sCachedFonts.get(familyName); 37 | } 38 | 39 | return Typeface.create(familyName, style); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/Button.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import androidx.annotation.NonNull; 6 | import androidx.appcompat.widget.AppCompatButton; 7 | 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | 11 | import com.rey.material.app.ThemeManager; 12 | import com.rey.material.drawable.RippleDrawable; 13 | import com.rey.material.util.ViewUtil; 14 | 15 | public class Button extends AppCompatButton implements ThemeManager.OnThemeChangedListener{ 16 | 17 | private RippleManager mRippleManager; 18 | 19 | protected int mStyleId; 20 | protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED; 21 | 22 | public Button(Context context) { 23 | super(context); 24 | 25 | init(context, null, 0, 0); 26 | } 27 | 28 | public Button(Context context, AttributeSet attrs) { 29 | super(context, attrs); 30 | 31 | init(context, attrs, 0, 0); 32 | } 33 | 34 | public Button(Context context, AttributeSet attrs, int defStyleAttr) { 35 | super(context, attrs, defStyleAttr); 36 | 37 | init(context, attrs, defStyleAttr, 0); 38 | } 39 | 40 | protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 41 | ViewUtil.applyFont(this, attrs, defStyleAttr, defStyleRes); 42 | applyStyle(context, attrs, defStyleAttr, defStyleRes); 43 | if(!isInEditMode()) 44 | mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes); 45 | } 46 | 47 | public void applyStyle(int resId){ 48 | ViewUtil.applyStyle(this, resId); 49 | applyStyle(getContext(), null, 0, resId); 50 | } 51 | 52 | protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 53 | getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes); 54 | } 55 | 56 | @Override 57 | public void setTextAppearance(int resId) { 58 | ViewUtil.applyTextAppearance(this, resId); 59 | } 60 | 61 | @Override 62 | public void setTextAppearance(Context context, int resId) { 63 | ViewUtil.applyTextAppearance(this, resId); 64 | } 65 | 66 | @Override 67 | public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) { 68 | int style = ThemeManager.getInstance().getCurrentStyle(mStyleId); 69 | if(mCurrentStyle != style){ 70 | mCurrentStyle = style; 71 | applyStyle(mCurrentStyle); 72 | } 73 | } 74 | 75 | @Override 76 | protected void onAttachedToWindow() { 77 | super.onAttachedToWindow(); 78 | if(mStyleId != 0) { 79 | ThemeManager.getInstance().registerOnThemeChangedListener(this); 80 | onThemeChanged(null); 81 | } 82 | } 83 | 84 | @Override 85 | protected void onDetachedFromWindow() { 86 | super.onDetachedFromWindow(); 87 | RippleManager.cancelRipple(this); 88 | if(mStyleId != 0) 89 | ThemeManager.getInstance().unregisterOnThemeChangedListener(this); 90 | } 91 | 92 | @Override 93 | public void setBackgroundDrawable(Drawable drawable) { 94 | Drawable background = getBackground(); 95 | if(background instanceof RippleDrawable && !(drawable instanceof RippleDrawable)) 96 | ((RippleDrawable) background).setBackgroundDrawable(drawable); 97 | else 98 | super.setBackgroundDrawable(drawable); 99 | } 100 | 101 | protected RippleManager getRippleManager(){ 102 | if(mRippleManager == null){ 103 | synchronized (RippleManager.class){ 104 | if(mRippleManager == null) 105 | mRippleManager = new RippleManager(); 106 | } 107 | } 108 | 109 | return mRippleManager; 110 | } 111 | 112 | @Override 113 | public void setOnClickListener(OnClickListener l) { 114 | RippleManager rippleManager = getRippleManager(); 115 | if (l == rippleManager) 116 | super.setOnClickListener(l); 117 | else { 118 | rippleManager.setOnClickListener(l); 119 | setOnClickListener(rippleManager); 120 | } 121 | } 122 | 123 | @Override 124 | public boolean onTouchEvent(@NonNull MotionEvent event) { 125 | boolean result = super.onTouchEvent(event); 126 | return getRippleManager().onTouchEvent(this, event) || result; 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/CheckBox.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import com.rey.material.drawable.CheckBoxDrawable; 7 | 8 | public class CheckBox extends CompoundButton { 9 | 10 | public CheckBox(Context context) { 11 | super(context); 12 | } 13 | 14 | public CheckBox(Context context, AttributeSet attrs) { 15 | super(context, attrs); 16 | } 17 | 18 | public CheckBox(Context context, AttributeSet attrs, int defStyleAttr) { 19 | super(context, attrs, defStyleAttr); 20 | } 21 | 22 | @Override 23 | protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 24 | super.applyStyle(context, attrs, defStyleAttr, defStyleRes); 25 | 26 | CheckBoxDrawable drawable = new CheckBoxDrawable.Builder(context, attrs, defStyleAttr, defStyleRes).build(); 27 | drawable.setInEditMode(isInEditMode()); 28 | drawable.setAnimEnable(false); 29 | setButtonDrawable(drawable); 30 | drawable.setAnimEnable(true); 31 | } 32 | 33 | /** 34 | * Change the checked state of this button immediately without showing animation. 35 | * @param checked The checked state. 36 | */ 37 | public void setCheckedImmediately(boolean checked){ 38 | if(getButtonDrawable() instanceof CheckBoxDrawable){ 39 | CheckBoxDrawable drawable = (CheckBoxDrawable)getButtonDrawable(); 40 | drawable.setAnimEnable(false); 41 | setChecked(checked); 42 | drawable.setAnimEnable(true); 43 | } 44 | else 45 | setChecked(checked); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/CheckedImageView.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.Checkable; 6 | 7 | /** 8 | * Created by Rey on 9/16/2015. 9 | */ 10 | public class CheckedImageView extends ImageView implements Checkable { 11 | 12 | private boolean mChecked = false; 13 | 14 | private static final int[] STATE_CHECKED = new int[]{ 15 | android.R.attr.state_checked 16 | }; 17 | 18 | public CheckedImageView(Context context) { 19 | super(context); 20 | } 21 | 22 | public CheckedImageView(Context context, AttributeSet attrs) { 23 | super(context, attrs); 24 | } 25 | 26 | public CheckedImageView(Context context, AttributeSet attrs, int defStyleAttr) { 27 | super(context, attrs, defStyleAttr); 28 | } 29 | 30 | @Override 31 | public void setChecked(boolean b) { 32 | if(mChecked != b){ 33 | mChecked = b; 34 | refreshDrawableState(); 35 | } 36 | } 37 | 38 | @Override 39 | public boolean isChecked() { 40 | return mChecked; 41 | } 42 | 43 | @Override 44 | public void toggle() { 45 | setChecked(!isChecked()); 46 | } 47 | 48 | @Override 49 | public int[] onCreateDrawableState(int extraSpace) { 50 | final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); 51 | 52 | int[] additionalStates = mChecked ? STATE_CHECKED : null; 53 | if (additionalStates != null) 54 | mergeDrawableStates(drawableState, additionalStates); 55 | 56 | return drawableState; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/CheckedTextView.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import androidx.annotation.NonNull; 6 | import androidx.appcompat.widget.AppCompatCheckedTextView; 7 | 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | 11 | import com.rey.material.app.ThemeManager; 12 | import com.rey.material.drawable.RippleDrawable; 13 | import com.rey.material.util.ViewUtil; 14 | 15 | public class CheckedTextView extends AppCompatCheckedTextView implements ThemeManager.OnThemeChangedListener { 16 | 17 | private RippleManager mRippleManager; 18 | protected int mStyleId; 19 | protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED; 20 | 21 | /** 22 | * Interface definition for a callback to be invoked when the checked state is changed. 23 | */ 24 | public interface OnCheckedChangeListener{ 25 | /** 26 | * Called when the checked state is changed. 27 | * @param view The CheckedTextView view. 28 | * @param checked The checked state. 29 | */ 30 | void onCheckedChanged(CheckedTextView view, boolean checked); 31 | } 32 | 33 | private OnCheckedChangeListener mOnCheckedChangeListener; 34 | 35 | public CheckedTextView(Context context) { 36 | super(context); 37 | 38 | init(context, null, 0, 0); 39 | } 40 | 41 | public CheckedTextView(Context context, AttributeSet attrs) { 42 | super(context, attrs); 43 | 44 | init(context, attrs, 0, 0); 45 | } 46 | 47 | public CheckedTextView(Context context, AttributeSet attrs, int defStyleAttr) { 48 | super(context, attrs, defStyleAttr); 49 | 50 | init(context, attrs, defStyleAttr, 0); 51 | } 52 | 53 | protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 54 | ViewUtil.applyFont(this, attrs, defStyleAttr, defStyleRes); 55 | applyStyle(context, attrs, defStyleAttr, defStyleRes); 56 | if(!isInEditMode()) 57 | mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes); 58 | } 59 | 60 | public void applyStyle(int resId){ 61 | ViewUtil.applyStyle(this, resId); 62 | applyStyle(getContext(), null, 0, resId); 63 | } 64 | 65 | protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 66 | getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes); 67 | } 68 | 69 | /** 70 | * Set a listener will be called when the checked state is changed. 71 | * @param listener The {@link OnCheckedChangeListener} will be called. 72 | */ 73 | public void setOnCheckedChangeListener(OnCheckedChangeListener listener){ 74 | mOnCheckedChangeListener = listener; 75 | } 76 | 77 | @Override 78 | public void setChecked(boolean checked) { 79 | boolean change = isChecked() != checked; 80 | super.setChecked(checked); 81 | if(change && mOnCheckedChangeListener != null) 82 | mOnCheckedChangeListener.onCheckedChanged(this, checked); 83 | } 84 | 85 | @Override 86 | public void setTextAppearance(int resId) { 87 | ViewUtil.applyTextAppearance(this, resId); 88 | } 89 | 90 | @Override 91 | public void setTextAppearance(Context context, int resId) { 92 | ViewUtil.applyTextAppearance(this, resId); 93 | } 94 | 95 | @Override 96 | public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) { 97 | int style = ThemeManager.getInstance().getCurrentStyle(mStyleId); 98 | if(mCurrentStyle != style){ 99 | mCurrentStyle = style; 100 | applyStyle(mCurrentStyle); 101 | } 102 | } 103 | 104 | @Override 105 | protected void onAttachedToWindow() { 106 | super.onAttachedToWindow(); 107 | if(mStyleId != 0) { 108 | ThemeManager.getInstance().registerOnThemeChangedListener(this); 109 | onThemeChanged(null); 110 | } 111 | } 112 | 113 | @Override 114 | protected void onDetachedFromWindow() { 115 | super.onDetachedFromWindow(); 116 | RippleManager.cancelRipple(this); 117 | if(mStyleId != 0) 118 | ThemeManager.getInstance().unregisterOnThemeChangedListener(this); 119 | } 120 | 121 | @Override 122 | public void setBackgroundDrawable(Drawable drawable) { 123 | Drawable background = getBackground(); 124 | if(background instanceof RippleDrawable && !(drawable instanceof RippleDrawable)) 125 | ((RippleDrawable) background).setBackgroundDrawable(drawable); 126 | else 127 | super.setBackgroundDrawable(drawable); 128 | } 129 | 130 | protected RippleManager getRippleManager(){ 131 | if(mRippleManager == null){ 132 | synchronized (RippleManager.class){ 133 | if(mRippleManager == null) 134 | mRippleManager = new RippleManager(); 135 | } 136 | } 137 | 138 | return mRippleManager; 139 | } 140 | 141 | @Override 142 | public void setOnClickListener(OnClickListener l) { 143 | RippleManager rippleManager = getRippleManager(); 144 | if (l == rippleManager) 145 | super.setOnClickListener(l); 146 | else { 147 | rippleManager.setOnClickListener(l); 148 | setOnClickListener(rippleManager); 149 | } 150 | } 151 | 152 | @Override 153 | public boolean onTouchEvent(@NonNull MotionEvent event) { 154 | boolean result = super.onTouchEvent(event); 155 | return getRippleManager().onTouchEvent(this, event) || result; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/CircleCheckedTextView.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.Gravity; 6 | import android.view.animation.Interpolator; 7 | 8 | import androidx.appcompat.widget.AppCompatCheckedTextView; 9 | 10 | import com.rey.material.drawable.CircleDrawable; 11 | import com.rey.material.util.ViewUtil; 12 | 13 | /** 14 | * Created by Rey on 2/5/2015. 15 | */ 16 | public class CircleCheckedTextView extends AppCompatCheckedTextView { 17 | 18 | private CircleDrawable mBackground; 19 | 20 | public interface OnCheckedChangeListener{ 21 | void onCheckedChanged(CircleCheckedTextView view, boolean checked); 22 | } 23 | 24 | private OnCheckedChangeListener mCheckedChangeListener; 25 | 26 | public CircleCheckedTextView(Context context) { 27 | super(context); 28 | 29 | init(context, null, 0, 0); 30 | } 31 | 32 | public CircleCheckedTextView(Context context, AttributeSet attrs) { 33 | super(context, attrs); 34 | 35 | init(context, attrs, 0, 0); 36 | } 37 | 38 | public CircleCheckedTextView(Context context, AttributeSet attrs, int defStyleAttr) { 39 | super(context, attrs, defStyleAttr); 40 | 41 | init(context, attrs, defStyleAttr, 0); 42 | } 43 | 44 | protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 45 | setGravity(Gravity.CENTER); 46 | setPadding(0, 0, 0, 0); 47 | 48 | mBackground = new CircleDrawable(); 49 | mBackground.setInEditMode(isInEditMode()); 50 | mBackground.setAnimEnable(false); 51 | ViewUtil.setBackground(this, mBackground); 52 | mBackground.setAnimEnable(true); 53 | } 54 | 55 | public void setOnCheckedChangeListener(OnCheckedChangeListener listener){ 56 | mCheckedChangeListener = listener; 57 | } 58 | 59 | @Override 60 | public void setTextAppearance(int resId) { 61 | ViewUtil.applyTextAppearance(this, resId); 62 | } 63 | 64 | @Override 65 | public void setTextAppearance(Context context, int resId) { 66 | ViewUtil.applyTextAppearance(this, resId); 67 | } 68 | 69 | @Override 70 | public void setBackgroundColor(int color) { 71 | mBackground.setColor(color); 72 | } 73 | 74 | /** 75 | * Set the duration of background's animation. 76 | * @param duration The duration 77 | */ 78 | public void setAnimDuration(int duration) { 79 | mBackground.setAnimDuration(duration); 80 | } 81 | 82 | public void setInterpolator(Interpolator in, Interpolator out) { 83 | mBackground.setInterpolator(in, out); 84 | } 85 | 86 | @Override 87 | public void setChecked(boolean checked) { 88 | boolean oldCheck = isChecked(); 89 | 90 | if(oldCheck != checked) { 91 | super.setChecked(checked); 92 | 93 | if(mCheckedChangeListener != null) 94 | mCheckedChangeListener.onCheckedChanged(this, checked); 95 | } 96 | } 97 | 98 | public void setCheckedImmediately(boolean checked){ 99 | mBackground.setAnimEnable(false); 100 | setChecked(checked); 101 | mBackground.setAnimEnable(true); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/FrameLayout.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import androidx.annotation.NonNull; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | 9 | import com.rey.material.app.ThemeManager; 10 | import com.rey.material.drawable.RippleDrawable; 11 | import com.rey.material.util.ViewUtil; 12 | 13 | public class FrameLayout extends android.widget.FrameLayout implements ThemeManager.OnThemeChangedListener{ 14 | 15 | private RippleManager mRippleManager; 16 | 17 | protected int mStyleId; 18 | protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED; 19 | 20 | public FrameLayout(Context context) { 21 | super(context); 22 | 23 | init(context, null, 0, 0); 24 | } 25 | 26 | public FrameLayout(Context context, AttributeSet attrs) { 27 | super(context, attrs); 28 | 29 | init(context, attrs, 0, 0); 30 | } 31 | 32 | public FrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { 33 | super(context, attrs, defStyleAttr); 34 | 35 | init(context, attrs, defStyleAttr, 0); 36 | } 37 | 38 | protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 39 | applyStyle(context, attrs, defStyleAttr, defStyleRes); 40 | if(!isInEditMode()) 41 | mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes); 42 | } 43 | 44 | public void applyStyle(int resId){ 45 | ViewUtil.applyStyle(this, resId); 46 | applyStyle(getContext(), null, 0, resId); 47 | } 48 | 49 | protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 50 | getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes); 51 | } 52 | 53 | @Override 54 | public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) { 55 | int style = ThemeManager.getInstance().getCurrentStyle(mStyleId); 56 | if(mCurrentStyle != style){ 57 | mCurrentStyle = style; 58 | applyStyle(mCurrentStyle); 59 | } 60 | } 61 | 62 | @Override 63 | protected void onAttachedToWindow() { 64 | super.onAttachedToWindow(); 65 | if(mStyleId != 0) { 66 | ThemeManager.getInstance().registerOnThemeChangedListener(this); 67 | onThemeChanged(null); 68 | } 69 | } 70 | 71 | @Override 72 | protected void onDetachedFromWindow() { 73 | super.onDetachedFromWindow(); 74 | RippleManager.cancelRipple(this); 75 | if(mStyleId != 0) 76 | ThemeManager.getInstance().unregisterOnThemeChangedListener(this); 77 | } 78 | 79 | @Override 80 | public void setBackgroundDrawable(Drawable drawable) { 81 | Drawable background = getBackground(); 82 | if(background instanceof RippleDrawable && !(drawable instanceof RippleDrawable)) 83 | ((RippleDrawable) background).setBackgroundDrawable(drawable); 84 | else 85 | super.setBackgroundDrawable(drawable); 86 | } 87 | 88 | protected RippleManager getRippleManager(){ 89 | if(mRippleManager == null){ 90 | synchronized (RippleManager.class){ 91 | if(mRippleManager == null) 92 | mRippleManager = new RippleManager(); 93 | } 94 | } 95 | 96 | return mRippleManager; 97 | } 98 | 99 | @Override 100 | public void setOnClickListener(OnClickListener l) { 101 | RippleManager rippleManager = getRippleManager(); 102 | if (l == rippleManager) 103 | super.setOnClickListener(l); 104 | else { 105 | rippleManager.setOnClickListener(l); 106 | setOnClickListener(rippleManager); 107 | } 108 | } 109 | 110 | @Override 111 | public boolean onTouchEvent(@NonNull MotionEvent event) { 112 | boolean result = super.onTouchEvent(event); 113 | return getRippleManager().onTouchEvent(this, event) || result; 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/ImageButton.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import androidx.annotation.NonNull; 6 | import androidx.appcompat.widget.AppCompatImageButton; 7 | 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | 11 | import com.rey.material.app.ThemeManager; 12 | import com.rey.material.drawable.RippleDrawable; 13 | import com.rey.material.util.ViewUtil; 14 | 15 | public class ImageButton extends AppCompatImageButton implements ThemeManager.OnThemeChangedListener{ 16 | 17 | private RippleManager mRippleManager; 18 | protected int mStyleId; 19 | protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED; 20 | 21 | public ImageButton(Context context) { 22 | super(context); 23 | 24 | init(context, null, 0, 0); 25 | } 26 | 27 | public ImageButton(Context context, AttributeSet attrs) { 28 | super(context, attrs); 29 | 30 | init(context, attrs, 0, 0); 31 | } 32 | 33 | public ImageButton(Context context, AttributeSet attrs, int defStyleAttr) { 34 | super(context, attrs, defStyleAttr); 35 | 36 | init(context, attrs, defStyleAttr, 0); 37 | } 38 | 39 | protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 40 | applyStyle(context, attrs, defStyleAttr, defStyleRes); 41 | if(!isInEditMode()) 42 | mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes); 43 | } 44 | 45 | public void applyStyle(int resId){ 46 | ViewUtil.applyStyle(this, resId); 47 | applyStyle(getContext(), null, 0, resId); 48 | } 49 | 50 | protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 51 | getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes); 52 | } 53 | 54 | @Override 55 | public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) { 56 | int style = ThemeManager.getInstance().getCurrentStyle(mStyleId); 57 | if(mCurrentStyle != style){ 58 | mCurrentStyle = style; 59 | applyStyle(mCurrentStyle); 60 | } 61 | } 62 | 63 | @Override 64 | protected void onAttachedToWindow() { 65 | super.onAttachedToWindow(); 66 | if(mStyleId != 0) { 67 | ThemeManager.getInstance().registerOnThemeChangedListener(this); 68 | onThemeChanged(null); 69 | } 70 | } 71 | 72 | @Override 73 | protected void onDetachedFromWindow() { 74 | super.onDetachedFromWindow(); 75 | RippleManager.cancelRipple(this); 76 | if(mStyleId != 0) 77 | ThemeManager.getInstance().unregisterOnThemeChangedListener(this); 78 | } 79 | 80 | @Override 81 | public void setBackgroundDrawable(Drawable drawable) { 82 | Drawable background = getBackground(); 83 | if(background instanceof RippleDrawable && !(drawable instanceof RippleDrawable)) 84 | ((RippleDrawable) background).setBackgroundDrawable(drawable); 85 | else 86 | super.setBackgroundDrawable(drawable); 87 | } 88 | 89 | protected RippleManager getRippleManager(){ 90 | if(mRippleManager == null){ 91 | synchronized (RippleManager.class){ 92 | if(mRippleManager == null) 93 | mRippleManager = new RippleManager(); 94 | } 95 | } 96 | 97 | return mRippleManager; 98 | } 99 | 100 | @Override 101 | public void setOnClickListener(OnClickListener l) { 102 | RippleManager rippleManager = getRippleManager(); 103 | if (l == rippleManager) 104 | super.setOnClickListener(l); 105 | else { 106 | rippleManager.setOnClickListener(l); 107 | setOnClickListener(rippleManager); 108 | } 109 | } 110 | 111 | @Override 112 | public boolean onTouchEvent(@NonNull MotionEvent event) { 113 | boolean result = super.onTouchEvent(event); 114 | return getRippleManager().onTouchEvent(this, event) || result; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/ImageView.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import androidx.annotation.NonNull; 6 | import androidx.appcompat.widget.AppCompatImageView; 7 | 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | 11 | import com.rey.material.app.ThemeManager; 12 | import com.rey.material.drawable.RippleDrawable; 13 | import com.rey.material.util.ViewUtil; 14 | 15 | /** 16 | * Created by Rey on 9/16/2015. 17 | */ 18 | public class ImageView extends AppCompatImageView implements ThemeManager.OnThemeChangedListener { 19 | 20 | private RippleManager mRippleManager; 21 | protected int mStyleId; 22 | protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED; 23 | 24 | public ImageView(Context context) { 25 | super(context); 26 | 27 | init(context, null, 0, 0); 28 | } 29 | 30 | public ImageView(Context context, AttributeSet attrs) { 31 | super(context, attrs); 32 | 33 | init(context, attrs, 0, 0); 34 | } 35 | 36 | public ImageView(Context context, AttributeSet attrs, int defStyleAttr) { 37 | super(context, attrs, defStyleAttr); 38 | 39 | init(context, attrs, defStyleAttr, 0); 40 | } 41 | 42 | protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 43 | applyStyle(context, attrs, defStyleAttr, defStyleRes); 44 | if(!isInEditMode()) 45 | mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes); 46 | } 47 | 48 | public void applyStyle(int resId){ 49 | ViewUtil.applyStyle(this, resId); 50 | applyStyle(getContext(), null, 0, resId); 51 | } 52 | 53 | protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 54 | getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes); 55 | } 56 | 57 | @Override 58 | public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) { 59 | int style = ThemeManager.getInstance().getCurrentStyle(mStyleId); 60 | if(mCurrentStyle != style){ 61 | mCurrentStyle = style; 62 | applyStyle(mCurrentStyle); 63 | } 64 | } 65 | 66 | @Override 67 | protected void onAttachedToWindow() { 68 | super.onAttachedToWindow(); 69 | if(mStyleId != 0) { 70 | ThemeManager.getInstance().registerOnThemeChangedListener(this); 71 | onThemeChanged(null); 72 | } 73 | } 74 | 75 | @Override 76 | protected void onDetachedFromWindow() { 77 | super.onDetachedFromWindow(); 78 | RippleManager.cancelRipple(this); 79 | if(mStyleId != 0) 80 | ThemeManager.getInstance().unregisterOnThemeChangedListener(this); 81 | } 82 | 83 | @Override 84 | public void setBackgroundDrawable(Drawable drawable) { 85 | Drawable background = getBackground(); 86 | if(background instanceof RippleDrawable && !(drawable instanceof RippleDrawable)) 87 | ((RippleDrawable) background).setBackgroundDrawable(drawable); 88 | else 89 | super.setBackgroundDrawable(drawable); 90 | } 91 | 92 | protected RippleManager getRippleManager(){ 93 | if(mRippleManager == null){ 94 | synchronized (RippleManager.class){ 95 | if(mRippleManager == null) 96 | mRippleManager = new RippleManager(); 97 | } 98 | } 99 | 100 | return mRippleManager; 101 | } 102 | 103 | @Override 104 | public void setOnClickListener(OnClickListener l) { 105 | RippleManager rippleManager = getRippleManager(); 106 | if (l == rippleManager) 107 | super.setOnClickListener(l); 108 | else { 109 | rippleManager.setOnClickListener(l); 110 | setOnClickListener(rippleManager); 111 | } 112 | } 113 | 114 | @Override 115 | public boolean onTouchEvent(@NonNull MotionEvent event) { 116 | boolean result = super.onTouchEvent(event); 117 | return getRippleManager().onTouchEvent(this, event) || result; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/LinearLayout.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.graphics.drawable.Drawable; 6 | import android.os.Build; 7 | import androidx.annotation.NonNull; 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | 11 | import com.rey.material.app.ThemeManager; 12 | import com.rey.material.drawable.RippleDrawable; 13 | import com.rey.material.util.ViewUtil; 14 | 15 | public class LinearLayout extends android.widget.LinearLayout implements ThemeManager.OnThemeChangedListener{ 16 | 17 | private RippleManager mRippleManager; 18 | 19 | protected int mStyleId; 20 | protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED; 21 | 22 | public LinearLayout(Context context) { 23 | super(context); 24 | 25 | init(context, null, 0, 0); 26 | } 27 | 28 | public LinearLayout(Context context, AttributeSet attrs) { 29 | super(context, attrs); 30 | 31 | init(context, attrs, 0, 0); 32 | } 33 | 34 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 35 | public LinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { 36 | super(context, attrs, defStyleAttr); 37 | 38 | init(context, attrs, defStyleAttr, 0); 39 | } 40 | 41 | protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 42 | applyStyle(context, attrs, defStyleAttr, defStyleRes); 43 | if(!isInEditMode()) 44 | mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes); 45 | } 46 | 47 | public void applyStyle(int resId){ 48 | ViewUtil.applyStyle(this, resId); 49 | applyStyle(getContext(), null, 0, resId); 50 | } 51 | 52 | protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 53 | getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes); 54 | } 55 | 56 | @Override 57 | public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) { 58 | int style = ThemeManager.getInstance().getCurrentStyle(mStyleId); 59 | if(mCurrentStyle != style){ 60 | mCurrentStyle = style; 61 | applyStyle(mCurrentStyle); 62 | } 63 | } 64 | 65 | @Override 66 | protected void onAttachedToWindow() { 67 | super.onAttachedToWindow(); 68 | if(mStyleId != 0) { 69 | ThemeManager.getInstance().registerOnThemeChangedListener(this); 70 | onThemeChanged(null); 71 | } 72 | } 73 | 74 | @Override 75 | protected void onDetachedFromWindow() { 76 | super.onDetachedFromWindow(); 77 | RippleManager.cancelRipple(this); 78 | if(mStyleId != 0) 79 | ThemeManager.getInstance().unregisterOnThemeChangedListener(this); 80 | } 81 | 82 | @Override 83 | public void setBackgroundDrawable(Drawable drawable) { 84 | Drawable background = getBackground(); 85 | if(background instanceof RippleDrawable && !(drawable instanceof RippleDrawable)) 86 | ((RippleDrawable) background).setBackgroundDrawable(drawable); 87 | else 88 | super.setBackgroundDrawable(drawable); 89 | } 90 | 91 | protected RippleManager getRippleManager(){ 92 | if(mRippleManager == null){ 93 | synchronized (RippleManager.class){ 94 | if(mRippleManager == null) 95 | mRippleManager = new RippleManager(); 96 | } 97 | } 98 | 99 | return mRippleManager; 100 | } 101 | 102 | @Override 103 | public void setOnClickListener(OnClickListener l) { 104 | RippleManager rippleManager = getRippleManager(); 105 | if (l == rippleManager) 106 | super.setOnClickListener(l); 107 | else { 108 | rippleManager.setOnClickListener(l); 109 | setOnClickListener(rippleManager); 110 | } 111 | } 112 | 113 | @Override 114 | public boolean onTouchEvent(@NonNull MotionEvent event) { 115 | boolean result = super.onTouchEvent(event); 116 | return getRippleManager().onTouchEvent(this, event) || result; 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/ListView.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | 7 | import androidx.appcompat.widget.ListViewCompat; 8 | 9 | import com.rey.material.app.ThemeManager; 10 | import com.rey.material.util.ViewUtil; 11 | 12 | public class ListView extends ListViewCompat implements ThemeManager.OnThemeChangedListener{ 13 | 14 | private RecyclerListener mRecyclerListener; 15 | 16 | protected int mStyleId; 17 | protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED; 18 | 19 | public ListView(Context context) { 20 | super(context); 21 | 22 | init(context, null, 0, 0); 23 | } 24 | 25 | public ListView(Context context, AttributeSet attrs) { 26 | super(context, attrs); 27 | 28 | init(context, attrs, 0, 0); 29 | } 30 | 31 | public ListView(Context context, AttributeSet attrs, int defStyleAttr) { 32 | super(context, attrs, defStyleAttr); 33 | 34 | init(context, attrs, defStyleAttr, 0); 35 | } 36 | 37 | protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 38 | 39 | super.setRecyclerListener(new RecyclerListener() { 40 | 41 | @Override 42 | public void onMovedToScrapHeap(View view) { 43 | RippleManager.cancelRipple(view); 44 | 45 | if(mRecyclerListener != null) 46 | mRecyclerListener.onMovedToScrapHeap(view); 47 | } 48 | 49 | }); 50 | 51 | if(!isInEditMode()) 52 | mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes); 53 | } 54 | 55 | public void applyStyle(int resId){ 56 | ViewUtil.applyStyle(this, resId); 57 | applyStyle(getContext(), null, 0, resId); 58 | } 59 | 60 | protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 61 | } 62 | 63 | @Override 64 | public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) { 65 | int style = ThemeManager.getInstance().getCurrentStyle(mStyleId); 66 | if(mCurrentStyle != style){ 67 | mCurrentStyle = style; 68 | applyStyle(mCurrentStyle); 69 | } 70 | } 71 | 72 | @Override 73 | protected void onAttachedToWindow() { 74 | super.onAttachedToWindow(); 75 | if(mStyleId != 0) { 76 | ThemeManager.getInstance().registerOnThemeChangedListener(this); 77 | onThemeChanged(null); 78 | } 79 | } 80 | 81 | @Override 82 | protected void onDetachedFromWindow() { 83 | super.onDetachedFromWindow(); 84 | if(mStyleId != 0) 85 | ThemeManager.getInstance().unregisterOnThemeChangedListener(this); 86 | } 87 | 88 | @Override 89 | public void setRecyclerListener(RecyclerListener listener) { 90 | mRecyclerListener = listener; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/PopupWindow.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.content.res.TypedArray; 6 | import android.os.Build; 7 | import android.util.AttributeSet; 8 | import android.view.View; 9 | 10 | import com.rey.material.R; 11 | 12 | public class PopupWindow extends android.widget.PopupWindow { 13 | 14 | private final boolean mOverlapAnchor; 15 | 16 | public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) { 17 | super(context, attrs, defStyleAttr); 18 | 19 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PopupWindow, defStyleAttr, 0); 20 | mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false); 21 | a.recycle(); 22 | 23 | } 24 | 25 | @Override 26 | public void showAsDropDown(View anchor, int xoff, int yoff) { 27 | if (Build.VERSION.SDK_INT < 21 && mOverlapAnchor) { 28 | // If we're pre-L, emulate overlapAnchor by modifying the yOff 29 | yoff -= anchor.getHeight(); 30 | } 31 | super.showAsDropDown(anchor, xoff, yoff); 32 | } 33 | 34 | @TargetApi(Build.VERSION_CODES.KITKAT) 35 | @Override 36 | public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) { 37 | if (Build.VERSION.SDK_INT < 21 && mOverlapAnchor) { 38 | // If we're pre-L, emulate overlapAnchor by modifying the yOff 39 | yoff -= anchor.getHeight(); 40 | } 41 | super.showAsDropDown(anchor, xoff, yoff, gravity); 42 | } 43 | 44 | @Override 45 | public void update(View anchor, int xoff, int yoff, int width, int height) { 46 | if (Build.VERSION.SDK_INT < 21 && mOverlapAnchor) { 47 | // If we're pre-L, emulate overlapAnchor by modifying the yOff 48 | yoff -= anchor.getHeight(); 49 | } 50 | super.update(anchor, xoff, yoff, width, height); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/RadioButton.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import com.rey.material.drawable.RadioButtonDrawable; 7 | 8 | public class RadioButton extends CompoundButton { 9 | 10 | public RadioButton(Context context) { 11 | super(context); 12 | } 13 | 14 | public RadioButton(Context context, AttributeSet attrs) { 15 | super(context, attrs); 16 | } 17 | 18 | public RadioButton(Context context, AttributeSet attrs, int defStyleAttr) { 19 | super(context, attrs, defStyleAttr); 20 | } 21 | 22 | @Override 23 | protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 24 | super.applyStyle(context, attrs, defStyleAttr, defStyleRes); 25 | 26 | RadioButtonDrawable drawable = new RadioButtonDrawable.Builder(context, attrs, defStyleAttr, defStyleRes).build(); 27 | drawable.setInEditMode(isInEditMode()); 28 | drawable.setAnimEnable(false); 29 | setButtonDrawable(drawable); 30 | drawable.setAnimEnable(true); 31 | } 32 | 33 | @Override 34 | public void toggle() { 35 | // we override to prevent toggle when the radio is already 36 | // checked (as opposed to check boxes widgets) 37 | if (!isChecked()) { 38 | super.toggle(); 39 | } 40 | } 41 | 42 | /** 43 | * Change the checked state of this button immediately without showing animation. 44 | * @param checked The checked state. 45 | */ 46 | public void setCheckedImmediately(boolean checked){ 47 | if(getButtonDrawable() instanceof RadioButtonDrawable){ 48 | RadioButtonDrawable drawable = (RadioButtonDrawable)getButtonDrawable(); 49 | drawable.setAnimEnable(false); 50 | setChecked(checked); 51 | drawable.setAnimEnable(true); 52 | } 53 | else 54 | setChecked(checked); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/RelativeLayout.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import androidx.annotation.NonNull; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | 9 | import com.rey.material.app.ThemeManager; 10 | import com.rey.material.drawable.RippleDrawable; 11 | import com.rey.material.util.ViewUtil; 12 | 13 | public class RelativeLayout extends android.widget.RelativeLayout implements ThemeManager.OnThemeChangedListener{ 14 | 15 | private RippleManager mRippleManager; 16 | 17 | protected int mStyleId; 18 | protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED; 19 | 20 | public RelativeLayout(Context context) { 21 | super(context); 22 | 23 | init(context, null, 0, 0); 24 | } 25 | 26 | public RelativeLayout(Context context, AttributeSet attrs) { 27 | super(context, attrs); 28 | 29 | init(context, attrs, 0, 0); 30 | } 31 | 32 | public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { 33 | super(context, attrs, defStyleAttr); 34 | 35 | init(context, attrs, defStyleAttr, 0); 36 | } 37 | 38 | protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 39 | applyStyle(context, attrs, defStyleAttr, defStyleRes); 40 | if(!isInEditMode()) 41 | mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes); 42 | } 43 | 44 | public void applyStyle(int resId){ 45 | ViewUtil.applyStyle(this, resId); 46 | applyStyle(getContext(), null, 0, resId); 47 | } 48 | 49 | protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 50 | getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes); 51 | } 52 | 53 | @Override 54 | public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) { 55 | int style = ThemeManager.getInstance().getCurrentStyle(mStyleId); 56 | if(mCurrentStyle != style){ 57 | mCurrentStyle = style; 58 | applyStyle(mCurrentStyle); 59 | } 60 | } 61 | 62 | @Override 63 | protected void onAttachedToWindow() { 64 | super.onAttachedToWindow(); 65 | if(mStyleId != 0) { 66 | ThemeManager.getInstance().registerOnThemeChangedListener(this); 67 | onThemeChanged(null); 68 | } 69 | } 70 | 71 | @Override 72 | protected void onDetachedFromWindow() { 73 | super.onDetachedFromWindow(); 74 | RippleManager.cancelRipple(this); 75 | if(mStyleId != 0) 76 | ThemeManager.getInstance().unregisterOnThemeChangedListener(this); 77 | } 78 | 79 | @Override 80 | public void setBackgroundDrawable(Drawable drawable) { 81 | Drawable background = getBackground(); 82 | if(background instanceof RippleDrawable && !(drawable instanceof RippleDrawable)) 83 | ((RippleDrawable) background).setBackgroundDrawable(drawable); 84 | else 85 | super.setBackgroundDrawable(drawable); 86 | } 87 | 88 | protected RippleManager getRippleManager(){ 89 | if(mRippleManager == null){ 90 | synchronized (RippleManager.class){ 91 | if(mRippleManager == null) 92 | mRippleManager = new RippleManager(); 93 | } 94 | } 95 | 96 | return mRippleManager; 97 | } 98 | 99 | @Override 100 | public void setOnClickListener(OnClickListener l) { 101 | RippleManager rippleManager = getRippleManager(); 102 | if (l == rippleManager) 103 | super.setOnClickListener(l); 104 | else { 105 | rippleManager.setOnClickListener(l); 106 | setOnClickListener(rippleManager); 107 | } 108 | } 109 | 110 | @Override 111 | public boolean onTouchEvent(@NonNull MotionEvent event) { 112 | boolean result = super.onTouchEvent(event); 113 | return getRippleManager().onTouchEvent(this, event) || result; 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/RippleManager.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.drawable.Drawable; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import com.rey.material.R; 12 | import com.rey.material.drawable.RippleDrawable; 13 | import com.rey.material.drawable.ToolbarRippleDrawable; 14 | import com.rey.material.util.ViewUtil; 15 | 16 | public final class RippleManager implements View.OnClickListener{ 17 | 18 | private View.OnClickListener mClickListener; 19 | private boolean mClickScheduled = false; 20 | 21 | public RippleManager(){} 22 | 23 | /** 24 | * Should be called in the construction method of view to create a RippleDrawable. 25 | * @param v 26 | * @param context 27 | * @param attrs 28 | * @param defStyleAttr 29 | * @param defStyleRes 30 | */ 31 | public void onCreate(View v, Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 32 | if(v.isInEditMode()) 33 | return; 34 | 35 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RippleView, defStyleAttr, defStyleRes); 36 | int rippleStyle = a.getResourceId(R.styleable.RippleView_rd_style, 0); 37 | RippleDrawable drawable = null; 38 | 39 | if(rippleStyle != 0) 40 | drawable = new RippleDrawable.Builder(context, rippleStyle).backgroundDrawable(getBackground(v)).build(); 41 | else{ 42 | boolean rippleEnable = a.getBoolean(R.styleable.RippleView_rd_enable, false); 43 | if(rippleEnable) 44 | drawable = new RippleDrawable.Builder(context, attrs, defStyleAttr, defStyleRes).backgroundDrawable(getBackground(v)).build(); 45 | } 46 | 47 | a.recycle(); 48 | 49 | if(drawable != null) 50 | ViewUtil.setBackground(v, drawable); 51 | } 52 | 53 | private Drawable getBackground(View v){ 54 | Drawable background = v.getBackground(); 55 | if(background == null) 56 | return null; 57 | 58 | if(background instanceof RippleDrawable) 59 | return ((RippleDrawable)background).getBackgroundDrawable(); 60 | 61 | return background; 62 | } 63 | 64 | public void setOnClickListener(View.OnClickListener l) { 65 | mClickListener = l; 66 | } 67 | 68 | public boolean onTouchEvent(View v, MotionEvent event){ 69 | Drawable background = v.getBackground(); 70 | return background != null && background instanceof RippleDrawable && ((RippleDrawable) background).onTouch(v, event); 71 | } 72 | 73 | @Override 74 | public void onClick(View v) { 75 | Drawable background = v.getBackground(); 76 | long delay = 0; 77 | 78 | if(background != null) { 79 | if (background instanceof RippleDrawable) 80 | delay = ((RippleDrawable) background).getClickDelayTime(); 81 | else if (background instanceof ToolbarRippleDrawable) 82 | delay = ((ToolbarRippleDrawable) background).getClickDelayTime(); 83 | } 84 | 85 | if(delay > 0 && v.getHandler() != null) { 86 | if(!mClickScheduled){ 87 | mClickScheduled = true; 88 | v.getHandler().postDelayed(new ClickRunnable(v), delay); 89 | } 90 | } 91 | else 92 | dispatchClickEvent(v); 93 | } 94 | 95 | private void dispatchClickEvent(View v){ 96 | if(mClickListener != null) 97 | mClickListener.onClick(v); 98 | } 99 | 100 | /** 101 | * Cancel the ripple effect of this view and all of it's children. 102 | * @param v 103 | */ 104 | public static void cancelRipple(View v){ 105 | Drawable background = v.getBackground(); 106 | if(background instanceof RippleDrawable) 107 | ((RippleDrawable)background).cancel(); 108 | else if(background instanceof ToolbarRippleDrawable) 109 | ((ToolbarRippleDrawable)background).cancel(); 110 | 111 | if(v instanceof ViewGroup){ 112 | ViewGroup vg = (ViewGroup)v; 113 | for(int i = 0, count = vg.getChildCount(); i < count; i++) 114 | RippleManager.cancelRipple(vg.getChildAt(i)); 115 | } 116 | } 117 | 118 | class ClickRunnable implements Runnable{ 119 | View mView; 120 | 121 | public ClickRunnable(View v){ 122 | mView = v; 123 | } 124 | 125 | @Override 126 | public void run() { 127 | mClickScheduled = false; 128 | dispatchClickEvent(mView); 129 | } 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /material/src/main/java/com/rey/material/widget/TextView.java: -------------------------------------------------------------------------------- 1 | package com.rey.material.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.util.AttributeSet; 6 | import android.view.MotionEvent; 7 | import android.view.View; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.appcompat.widget.AppCompatTextView; 11 | 12 | import com.rey.material.app.ThemeManager; 13 | import com.rey.material.drawable.RippleDrawable; 14 | import com.rey.material.util.ViewUtil; 15 | 16 | public class TextView extends AppCompatTextView implements ThemeManager.OnThemeChangedListener{ 17 | 18 | private RippleManager mRippleManager; 19 | protected int mStyleId; 20 | protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED; 21 | 22 | public interface OnSelectionChangedListener{ 23 | void onSelectionChanged(View v, int selStart, int selEnd); 24 | } 25 | 26 | private OnSelectionChangedListener mOnSelectionChangedListener; 27 | 28 | public TextView(Context context) { 29 | super(context); 30 | 31 | init(context, null, 0, 0); 32 | } 33 | 34 | public TextView(Context context, AttributeSet attrs) { 35 | super(context, attrs); 36 | 37 | init(context, attrs, 0, 0); 38 | } 39 | 40 | public TextView(Context context, AttributeSet attrs, int defStyleAttr) { 41 | super(context, attrs, defStyleAttr); 42 | 43 | init(context, attrs, defStyleAttr, 0); 44 | } 45 | 46 | protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 47 | ViewUtil.applyFont(this, attrs, defStyleAttr, defStyleRes); 48 | applyStyle(context, attrs, defStyleAttr, defStyleRes); 49 | if(!isInEditMode()) 50 | mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes); 51 | } 52 | 53 | public void applyStyle(int resId){ 54 | ViewUtil.applyStyle(this, resId); 55 | applyStyle(getContext(), null, 0, resId); 56 | } 57 | 58 | protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 59 | getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes); 60 | } 61 | 62 | @Override 63 | public void setTextAppearance(int resId) { 64 | ViewUtil.applyTextAppearance(this, resId); 65 | } 66 | 67 | @Override 68 | public void setTextAppearance(Context context, int resId) { 69 | ViewUtil.applyTextAppearance(this, resId); 70 | } 71 | 72 | @Override 73 | public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) { 74 | int style = ThemeManager.getInstance().getCurrentStyle(mStyleId); 75 | if(mCurrentStyle != style){ 76 | mCurrentStyle = style; 77 | applyStyle(mCurrentStyle); 78 | } 79 | } 80 | 81 | @Override 82 | protected void onAttachedToWindow() { 83 | super.onAttachedToWindow(); 84 | if(mStyleId != 0) { 85 | ThemeManager.getInstance().registerOnThemeChangedListener(this); 86 | onThemeChanged(null); 87 | } 88 | } 89 | 90 | @Override 91 | protected void onDetachedFromWindow() { 92 | super.onDetachedFromWindow(); 93 | RippleManager.cancelRipple(this); 94 | if(mStyleId != 0) 95 | ThemeManager.getInstance().unregisterOnThemeChangedListener(this); 96 | } 97 | 98 | @Override 99 | public void setBackgroundDrawable(Drawable drawable) { 100 | Drawable background = getBackground(); 101 | if(background instanceof RippleDrawable && !(drawable instanceof RippleDrawable)) 102 | ((RippleDrawable) background).setBackgroundDrawable(drawable); 103 | else 104 | super.setBackgroundDrawable(drawable); 105 | } 106 | 107 | protected RippleManager getRippleManager(){ 108 | if(mRippleManager == null){ 109 | synchronized (RippleManager.class){ 110 | if(mRippleManager == null) 111 | mRippleManager = new RippleManager(); 112 | } 113 | } 114 | 115 | return mRippleManager; 116 | } 117 | 118 | @Override 119 | public void setOnClickListener(OnClickListener l) { 120 | RippleManager rippleManager = getRippleManager(); 121 | if (l == rippleManager) 122 | super.setOnClickListener(l); 123 | else { 124 | rippleManager.setOnClickListener(l); 125 | setOnClickListener(rippleManager); 126 | } 127 | } 128 | 129 | @Override 130 | public boolean onTouchEvent(@NonNull MotionEvent event) { 131 | boolean result = super.onTouchEvent(event); 132 | return getRippleManager().onTouchEvent(this, event) || result; 133 | } 134 | 135 | public void setOnSelectionChangedListener(OnSelectionChangedListener listener){ 136 | mOnSelectionChangedListener = listener; 137 | } 138 | 139 | @Override 140 | protected void onSelectionChanged(int selStart, int selEnd) { 141 | super.onSelectionChanged(selStart, selEnd); 142 | 143 | if(mOnSelectionChangedListener != null) 144 | mOnSelectionChangedListener.onSelectionChanged(this, selStart, selEnd); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /material/src/main/res/xml/nav_states.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 0 7 | 0.15 8 | 1 9 | 0.15 10 | 0 11 | 0.5 12 | 1 13 | 0.5 14 | 0 15 | 0.85 16 | 1 17 | 0.85 18 | 19 | 20 | 21 | 22 | 23 | 0.5 24 | 0 25 | 1 26 | 0.5 27 | 0 28 | 0.5 29 | 1 30 | 0.5 31 | 0.5 32 | 1 33 | 1 34 | 0.5 35 | 36 | 37 | 38 | 0 39 | 2 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':material' 2 | --------------------------------------------------------------------------------