├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── net │ │ └── huannguyen │ │ └── conductorexample │ │ ├── activity │ │ └── MainActivity.java │ │ ├── controller │ │ └── BaseController.java │ │ ├── countrydetail │ │ ├── CountryDetailController.java │ │ ├── CountryDetailView.java │ │ └── DetailEventHandler.java │ │ ├── countrygrid │ │ ├── CountryGridController.java │ │ ├── CountryGridView.java │ │ └── GridEventHandler.java │ │ ├── misc │ │ ├── BundleBuilder.java │ │ └── FourThreeImageView.java │ │ ├── model │ │ └── Country.java │ │ └── transition │ │ ├── DetailPopAnimChangeHandler.java │ │ ├── DetailPopTransChangeHandler.java │ │ ├── DetailPushAnimChangeHandler.java │ │ ├── DetailPushTransChangeHandler.java │ │ └── Scale.java │ └── res │ ├── drawable │ ├── ic_arrow_back.xml │ ├── ic_favorite.xml │ └── ripple.xml │ ├── layout-land │ └── country_detail.xml │ ├── layout │ ├── activity_main.xml │ ├── country_detail.xml │ ├── country_grid.xml │ └── country_item.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── values-v21 │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea 5 | *.iml 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | /archive 11 | app/app.iml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repo contains a demo of how to use Conductor to build Android apps which is taked about in my series of blog posts on Medium: https://medium.com/@huan.nguyen/exploring-conductor-android-app-development-without-fragments-part-1-fce1eab0c9df 2 | 3 | This demo illustrates: 4 | - The use of Conductor to replace fragments in Android apps 5 | - How to implement view change animation using `AnimatorChangeHandler` and `TransitionChangeHandler` 6 | 7 | [![Conductor Demo Video](http://i.imgur.com/TEu32n2.png)](https://youtu.be/-a93q_uqNUU "Conductor demo") 8 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /app.iml -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "net.huannguyen.conductorexample" 8 | minSdkVersion 16 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | vectorDrawables.useSupportLibrary = true 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(dir: 'libs', include: ['*.jar']) 25 | compile 'com.android.support:appcompat-v7:25.3.1' 26 | compile 'com.android.support:design:25.3.1' 27 | compile 'com.android.support:recyclerview-v7:25.3.1' 28 | compile 'com.bluelinelabs:conductor:2.1.2' 29 | compile 'com.jakewharton:butterknife:8.4.0' 30 | compile 'com.jakewharton:butterknife-compiler:8.4.0' 31 | compile 'com.squareup.picasso:picasso:2.5.2' 32 | } 33 | -------------------------------------------------------------------------------- /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 /Users/huannguyen/Library/Android/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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.activity; 20 | 21 | import android.os.Bundle; 22 | import android.support.v7.app.AppCompatActivity; 23 | import android.support.v7.widget.Toolbar; 24 | import android.view.MenuItem; 25 | import android.view.ViewGroup; 26 | 27 | import com.bluelinelabs.conductor.Conductor; 28 | import com.bluelinelabs.conductor.Router; 29 | import com.bluelinelabs.conductor.RouterTransaction; 30 | 31 | import net.huannguyen.conductorexample.R; 32 | import net.huannguyen.conductorexample.countrygrid.CountryGridController; 33 | 34 | import butterknife.BindView; 35 | import butterknife.ButterKnife; 36 | 37 | public final class MainActivity extends AppCompatActivity { 38 | 39 | @BindView(R.id.toolbar) 40 | Toolbar toolbar; 41 | @BindView(R.id.controller_container) ViewGroup container; 42 | 43 | private Router router; 44 | 45 | @Override 46 | protected void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | 49 | setContentView(R.layout.activity_main); 50 | ButterKnife.bind(this); 51 | 52 | setSupportActionBar(toolbar); 53 | 54 | router = Conductor.attachRouter(this, container, savedInstanceState); 55 | if (!router.hasRootController()) { 56 | router.setRoot(RouterTransaction.with(new CountryGridController())); 57 | } 58 | } 59 | 60 | @Override 61 | public void onBackPressed() { 62 | if (!router.handleBack()) { 63 | super.onBackPressed(); 64 | } 65 | } 66 | 67 | @Override 68 | public boolean onOptionsItemSelected(MenuItem item) { 69 | if (item.getItemId() == android.R.id.home) { 70 | onBackPressed(); 71 | return true; 72 | } 73 | return super.onOptionsItemSelected(item); 74 | } 75 | 76 | public void setToolBarTitle(String title) { 77 | toolbar.setTitle(title); 78 | } 79 | 80 | public void enableUpArrow(boolean enabled) { 81 | if (enabled) toolbar.setNavigationIcon(R.drawable.ic_arrow_back); 82 | else toolbar.setNavigationIcon(null); 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/controller/BaseController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.controller; 20 | 21 | import android.os.Bundle; 22 | import android.support.annotation.NonNull; 23 | import android.view.View; 24 | 25 | import com.bluelinelabs.conductor.Controller; 26 | 27 | import net.huannguyen.conductorexample.activity.MainActivity; 28 | 29 | public abstract class BaseController extends Controller { 30 | 31 | protected BaseController() { } 32 | protected BaseController(Bundle args) { 33 | super(args); 34 | } 35 | 36 | @Override 37 | protected void onAttach(@NonNull View view) { 38 | super.onAttach(view); 39 | 40 | // Quick way to access the toolbar for demo purposes. Production app needs to have this done properly 41 | MainActivity activity = (MainActivity) getActivity(); 42 | 43 | // Activity should have already been set after the conductor is attached. 44 | assert activity != null; 45 | 46 | activity.setToolBarTitle(getTitle()); 47 | activity.enableUpArrow(getRouter().getBackstackSize() > 1); 48 | } 49 | 50 | protected abstract String getTitle(); 51 | } -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/countrydetail/CountryDetailController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.countrydetail; 20 | 21 | import android.content.Intent; 22 | import android.net.Uri; 23 | import android.os.Bundle; 24 | import android.support.annotation.NonNull; 25 | import android.view.LayoutInflater; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | 29 | import net.huannguyen.conductorexample.R; 30 | import net.huannguyen.conductorexample.controller.BaseController; 31 | import net.huannguyen.conductorexample.misc.BundleBuilder; 32 | import net.huannguyen.conductorexample.model.Country; 33 | 34 | public class CountryDetailController extends BaseController implements DetailEventHandler { 35 | 36 | private static final String KEY_COUNTRY = "KEY_COUNTRY"; 37 | 38 | private Country country = getArgs().getParcelable(KEY_COUNTRY); 39 | 40 | public CountryDetailController(Country country) { 41 | this(new BundleBuilder(new Bundle()) 42 | .putParcelable(KEY_COUNTRY, country) 43 | .build()); 44 | } 45 | 46 | public CountryDetailController(Bundle args) { 47 | super(args); 48 | } 49 | 50 | @NonNull 51 | @Override 52 | protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { 53 | CountryDetailView view = (CountryDetailView) inflater.inflate(R.layout.country_detail, container, false); 54 | view.setEventHandler(this); 55 | view.setData(country); 56 | return view; 57 | } 58 | 59 | @Override 60 | public void onFlagClicked() { 61 | Intent mapIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(String.format("geo:0,0?q=%s", country.getName()))); 62 | mapIntent.setPackage("com.google.android.apps.maps"); 63 | if (mapIntent.resolveActivity(getActivity().getPackageManager()) != null) { 64 | startActivity(mapIntent); 65 | } 66 | } 67 | 68 | @Override 69 | protected String getTitle() { 70 | return country.getName(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/countrydetail/CountryDetailView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.countrydetail; 20 | 21 | import android.content.Context; 22 | import android.support.annotation.NonNull; 23 | import android.support.annotation.Nullable; 24 | import android.support.design.widget.FloatingActionButton; 25 | import android.util.AttributeSet; 26 | import android.view.ViewGroup; 27 | import android.widget.ImageView; 28 | import android.widget.LinearLayout; 29 | import android.widget.TextView; 30 | 31 | import com.squareup.picasso.Picasso; 32 | 33 | import net.huannguyen.conductorexample.R; 34 | import net.huannguyen.conductorexample.model.Country; 35 | 36 | import butterknife.BindView; 37 | import butterknife.ButterKnife; 38 | import butterknife.OnClick; 39 | 40 | public class CountryDetailView extends LinearLayout { 41 | 42 | // Note: Having the controller implementing an interface and pass its reference to this View to handle navigation 43 | // upon clicks for demo purposes. 44 | // A nicer way of doing this is using RxJava to turn view clicks into a stream which is then observed by a Presenter 45 | // declared in the Controller. The Presenter then determines what should be done to response to clicks. 46 | private DetailEventHandler eventHandler; 47 | 48 | @BindView(R.id.capital) 49 | TextView capitalView; 50 | 51 | @BindView(R.id.population) 52 | TextView populationView; 53 | 54 | @BindView(R.id.currency) 55 | TextView currencyView; 56 | 57 | @BindView(R.id.language) 58 | TextView languageView; 59 | 60 | @BindView(R.id.timezone) 61 | TextView timezoneView; 62 | 63 | // Assign public visibility for the below views for quickly demo view change animation. 64 | // Production apps should have this done properly. 65 | @BindView(R.id.flag) 66 | public ImageView flagView; 67 | 68 | @BindView(R.id.favourite_fab) 69 | public FloatingActionButton favouriteFab; 70 | 71 | @BindView(R.id.detail_group) 72 | public ViewGroup detailGroup; 73 | 74 | @OnClick(R.id.flag) 75 | void onFlagClicked() { 76 | eventHandler.onFlagClicked(); 77 | } 78 | 79 | public CountryDetailView(Context context, @Nullable AttributeSet attrs) { 80 | super(context, attrs); 81 | } 82 | 83 | public void setEventHandler(DetailEventHandler eventHandler) { 84 | this.eventHandler = eventHandler; 85 | } 86 | 87 | @Override 88 | protected void onFinishInflate() { 89 | super.onFinishInflate(); 90 | ButterKnife.bind(this); 91 | } 92 | 93 | public void setData(@NonNull Country country) { 94 | Picasso.with(getContext()) 95 | .load(country.getFlag()) 96 | .into(flagView); 97 | 98 | capitalView.setText(country.getCapital()); 99 | populationView.setText(String.valueOf(country.getPopulation())); 100 | languageView.setText(country.getLanguage()); 101 | currencyView.setText(country.getCurrency()); 102 | timezoneView.setText(country.getTimezone()); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/countrydetail/DetailEventHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.countrydetail; 20 | 21 | public interface DetailEventHandler { 22 | void onFlagClicked(); 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/countrygrid/CountryGridController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.countrygrid; 20 | 21 | import android.support.annotation.NonNull; 22 | import android.view.LayoutInflater; 23 | import android.view.View; 24 | import android.view.ViewGroup; 25 | 26 | import com.bluelinelabs.conductor.ControllerChangeHandler; 27 | import com.bluelinelabs.conductor.RouterTransaction; 28 | import com.bluelinelabs.conductor.changehandler.TransitionChangeHandlerCompat; 29 | 30 | import net.huannguyen.conductorexample.R; 31 | import net.huannguyen.conductorexample.controller.BaseController; 32 | import net.huannguyen.conductorexample.countrydetail.CountryDetailController; 33 | import net.huannguyen.conductorexample.model.Country; 34 | import net.huannguyen.conductorexample.transition.DetailPopAnimChangeHandler; 35 | import net.huannguyen.conductorexample.transition.DetailPopTransChangeHandler; 36 | import net.huannguyen.conductorexample.transition.DetailPushAnimChangeHandler; 37 | import net.huannguyen.conductorexample.transition.DetailPushTransChangeHandler; 38 | 39 | public class CountryGridController extends BaseController implements GridEventHandler { 40 | 41 | @NonNull 42 | @Override 43 | protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { 44 | CountryGridView view = (CountryGridView) inflater.inflate(R.layout.country_grid, container, false); 45 | view.setEventHandler(this); 46 | return view; 47 | } 48 | 49 | @Override 50 | public void onCountryClicked(@NonNull Country country) { 51 | // For demo purposes, use animator change handler for countries with name starting with 52 | // a character before 'i' in the alphabet. For other countries, use transition change handler if the app is running on 53 | // API level 21+. Use the mentioned animator change handler otherwise. 54 | boolean countryNameFirstCharBeforeI = country.getName().toLowerCase().charAt(0) < 'i'; 55 | 56 | ControllerChangeHandler pushHandler 57 | = countryNameFirstCharBeforeI ? new DetailPushAnimChangeHandler() 58 | : new TransitionChangeHandlerCompat(new DetailPushTransChangeHandler(country.getName()), new DetailPushAnimChangeHandler()); 59 | 60 | ControllerChangeHandler popHandler 61 | = countryNameFirstCharBeforeI ? new DetailPopAnimChangeHandler() 62 | : new TransitionChangeHandlerCompat(new DetailPopTransChangeHandler(country.getName()), new DetailPopAnimChangeHandler()); 63 | 64 | getRouter().pushController(RouterTransaction.with(new CountryDetailController(country)) 65 | .pushChangeHandler(pushHandler) 66 | .popChangeHandler(popHandler)); 67 | } 68 | 69 | @Override 70 | protected String getTitle() { 71 | return getActivity().getString(R.string.countries); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/countrygrid/CountryGridView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.countrygrid; 20 | 21 | import android.content.Context; 22 | import android.content.res.Configuration; 23 | import android.os.Build; 24 | import android.support.annotation.NonNull; 25 | import android.support.annotation.Nullable; 26 | import android.support.v7.widget.GridLayoutManager; 27 | import android.support.v7.widget.RecyclerView; 28 | import android.util.AttributeSet; 29 | import android.view.LayoutInflater; 30 | import android.view.View; 31 | import android.view.ViewGroup; 32 | import android.widget.ImageView; 33 | import android.widget.TextView; 34 | 35 | import com.squareup.picasso.Picasso; 36 | 37 | import net.huannguyen.conductorexample.R; 38 | import net.huannguyen.conductorexample.model.Country; 39 | 40 | import java.util.Arrays; 41 | import java.util.List; 42 | 43 | import butterknife.BindView; 44 | import butterknife.ButterKnife; 45 | import butterknife.OnClick; 46 | 47 | public class CountryGridView extends RecyclerView { 48 | 49 | // Note: Having the controller implementing an interface and pass its reference to this View to handle navigation 50 | // upon clicks for demo purposes. 51 | // A nicer way of doing this is using RxJava to turn view clicks into a stream which is then observed by a Presenter 52 | // declared in the Controller. The Presenter then determines what should be done to response to clicks. 53 | private GridEventHandler eventHandler; 54 | 55 | // Assume there is a list of countries that has already been obtained. 56 | // Data referenced from restcountries.eu. Flag images from flagpedia.net. 57 | private static final List COUNTRIES = Arrays.asList( 58 | new Country("Australia", "Canberra", 24117360, "https://flagpedia.net/data/flags/normal/au.png", "English", "Australian dollar", "UTC+10:00"), 59 | new Country("Finland", "Helsinki", 5491817, "https://flagpedia.net/data/flags/normal/fi.png", "Finish", "Euro", "UTC+02:00"), 60 | new Country("France", "Paris", 66710000, "https://flagpedia.net/data/flags/normal/fr.png", "French", "Euro", "UTC+01:00"), 61 | new Country("Germany", "Berlin", 81770900, "https://flagpedia.net/data/flags/normal/de.png", "German", "Euro", "UTC+01:00"), 62 | new Country("Greece", "Athens", 10858018, "https://flagpedia.net/data/flags/normal/gr.png", "Greek", "Euro", "UTC+02:00"), 63 | new Country("Hungary", "Budapest", 9823000, "https://flagpedia.net/data/flags/normal/hu.png", "Hungarian", "Hungarian forint", "UTC+01:00"), 64 | new Country("Iceland", "Reykjavík", 334300, "https://flagpedia.net/data/flags/normal/is.png", "Icelandic", "Icelandic króna", "UTC+00:00"), 65 | new Country("Ireland", "Dublin", 6378000, "https://flagpedia.net/data/flags/normal/ie.png", "Irish", "Euro", "UTC+00:00"), 66 | new Country("Italy", "Rome", 60665551, "https://flagpedia.net/data/flags/normal/it.png", "Italian", "Euro", "UTC+01:00"), 67 | new Country("Luxembourg", "Luxembourg", 576200, "https://flagpedia.net/data/flags/normal/lu.png", "Luxembourgish", "Euro", "UTC+01:00"), 68 | new Country("Netherlands", "Amsterdam", 17019800, "https://flagpedia.net/data/flags/normal/nl.png", "Dutch", "Euro", "UTC+01:00"), 69 | new Country("Norway", "Oslo", 5223256, "https://flagpedia.net/data/flags/normal/no.png", "Norwegian", "Norwegian krone", "UTC+01:00"), 70 | new Country("Portugal", "Lisbon", 10374822, "https://flagpedia.net/data/flags/normal/pt.png", "Portuguese", "Euro", "UTC+00:00"), 71 | new Country("United Kingdom", "London", 65110000, "https://flagpedia.net/data/flags/normal/gb.png", "English", "Pound", "UTC+00:00")); 72 | 73 | public CountryGridView(Context context, @Nullable AttributeSet attrs) { 74 | super(context, attrs); 75 | init(); 76 | } 77 | 78 | private void init() { 79 | setLayoutManager(new GridLayoutManager(getContext(), 80 | getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ? 2: 3)); 81 | setAdapter(new CountryAdapter(COUNTRIES)); 82 | } 83 | 84 | private void onCountryClicked(Country country) { 85 | eventHandler.onCountryClicked(country); 86 | } 87 | 88 | public void setEventHandler(GridEventHandler eventHandler) { 89 | this.eventHandler = eventHandler; 90 | } 91 | 92 | private class CountryAdapter extends Adapter { 93 | 94 | private List countries; 95 | 96 | CountryAdapter(List countries) { 97 | this.countries = countries; 98 | } 99 | 100 | @Override 101 | public CountryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 102 | View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.country_item, parent, false); 103 | return new CountryViewHolder(itemView); 104 | } 105 | 106 | @Override 107 | public void onBindViewHolder(CountryViewHolder holder, int position) { 108 | holder.bindData(countries.get(position)); 109 | } 110 | 111 | @Override 112 | public int getItemCount() { 113 | return countries == null ? 0 : countries.size(); 114 | } 115 | } 116 | 117 | class CountryViewHolder extends ViewHolder { 118 | @BindView(R.id.flag) 119 | ImageView flagView; 120 | 121 | @BindView(R.id.name) 122 | TextView nameView; 123 | 124 | private Country country; 125 | 126 | @OnClick(R.id.flag) 127 | void onItemClicked() { 128 | onCountryClicked(country); 129 | } 130 | 131 | CountryViewHolder(View itemView) { 132 | super(itemView); 133 | ButterKnife.bind(this, itemView); 134 | } 135 | 136 | void bindData(@NonNull Country country) { 137 | this.country = country; 138 | Picasso.with(itemView.getContext()) 139 | .load(country.getFlag()) 140 | .into(flagView); 141 | nameView.setText(country.getName()); 142 | 143 | // Set transition name for flag view to enable transition animation. 144 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 145 | flagView.setTransitionName(country.getName()); 146 | } 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/countrygrid/GridEventHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.countrygrid; 20 | 21 | import android.support.annotation.NonNull; 22 | 23 | import net.huannguyen.conductorexample.model.Country; 24 | 25 | public interface GridEventHandler { 26 | void onCountryClicked(@NonNull Country country); 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/misc/BundleBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.misc; 20 | 21 | import android.os.Bundle; 22 | import android.os.Parcelable; 23 | 24 | public class BundleBuilder { 25 | private final Bundle bundle; 26 | 27 | public BundleBuilder(Bundle bundle) { 28 | this.bundle = bundle; 29 | } 30 | 31 | public BundleBuilder putParcelable(String key, Parcelable value) { 32 | bundle.putParcelable(key, value); 33 | return this; 34 | } 35 | 36 | public Bundle build() { 37 | return bundle; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/misc/FourThreeImageView.java: -------------------------------------------------------------------------------- 1 | package net.huannguyen.conductorexample.misc; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.AppCompatImageView; 5 | import android.util.AttributeSet; 6 | 7 | /** 8 | * An ImageView that is always 4:3 aspect ratio. 9 | * Code by Nick Butcher (https://github.com/nickbutcher) from Plaid app (https://github.com/nickbutcher/plaid). 10 | */ 11 | public class FourThreeImageView extends AppCompatImageView { 12 | 13 | public FourThreeImageView(Context context, AttributeSet attrs) { 14 | super(context, attrs); 15 | } 16 | 17 | @Override 18 | protected void onMeasure(int widthSpec, int heightSpec) { 19 | int fourThreeHeight = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthSpec) * 3 / 4, 20 | MeasureSpec.EXACTLY); 21 | super.onMeasure(widthSpec, fourThreeHeight); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/model/Country.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.model; 20 | 21 | import android.os.Parcel; 22 | import android.os.Parcelable; 23 | 24 | public final class Country implements Parcelable { 25 | private final String name; 26 | private final String capital; 27 | private final long population; 28 | private final String flag; 29 | private final String language; 30 | private final String currency; 31 | private final String timezone; 32 | 33 | public Country(String name, String capital, long population, String flag, String language, String currency, String timezone) { 34 | this.name = name; 35 | this.capital = capital; 36 | this.population = population; 37 | this.flag = flag; 38 | this.language = language; 39 | this.currency = currency; 40 | this.timezone = timezone; 41 | } 42 | 43 | private Country(Parcel in) { 44 | name = in.readString(); 45 | capital = in.readString(); 46 | population = in.readLong(); 47 | flag = in.readString(); 48 | language = in.readString(); 49 | currency = in.readString(); 50 | timezone = in.readString(); 51 | } 52 | 53 | public static final Creator CREATOR = new Creator() { 54 | @Override 55 | public Country createFromParcel(Parcel in) { 56 | return new Country(in); 57 | } 58 | 59 | @Override 60 | public Country[] newArray(int size) { 61 | return new Country[size]; 62 | } 63 | }; 64 | 65 | public String getName() { 66 | return name; 67 | } 68 | 69 | public String getCapital() { 70 | return capital; 71 | } 72 | 73 | public long getPopulation() { 74 | return population; 75 | } 76 | 77 | public String getFlag() { 78 | return flag; 79 | } 80 | 81 | public String getLanguage() { 82 | return language; 83 | } 84 | 85 | public String getCurrency() { 86 | return currency; 87 | } 88 | 89 | public String getTimezone() { 90 | return timezone; 91 | } 92 | 93 | @Override 94 | public int describeContents() { 95 | return 0; 96 | } 97 | 98 | @Override 99 | public void writeToParcel(Parcel dest, int flags) { 100 | dest.writeString(name); 101 | dest.writeString(capital); 102 | dest.writeLong(population); 103 | dest.writeString(flag); 104 | dest.writeString(language); 105 | dest.writeString(currency); 106 | dest.writeString(timezone); 107 | } 108 | } -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/transition/DetailPopAnimChangeHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.transition; 20 | 21 | import android.animation.Animator; 22 | import android.animation.AnimatorSet; 23 | import android.animation.ObjectAnimator; 24 | import android.animation.PropertyValuesHolder; 25 | import android.support.annotation.NonNull; 26 | import android.support.annotation.Nullable; 27 | import android.support.v4.view.animation.FastOutLinearInInterpolator; 28 | import android.view.View; 29 | import android.view.ViewGroup; 30 | 31 | import com.bluelinelabs.conductor.changehandler.AnimatorChangeHandler; 32 | 33 | import net.huannguyen.conductorexample.countrydetail.CountryDetailView; 34 | 35 | public class DetailPopAnimChangeHandler extends AnimatorChangeHandler { 36 | 37 | @NonNull 38 | @Override 39 | protected Animator getAnimator(@NonNull ViewGroup container, 40 | @Nullable View from, 41 | @Nullable View to, 42 | boolean isPush, 43 | boolean toAddedToContainer) { 44 | 45 | // Make sure the from view is a CountryDetailView 46 | if (from == null || !(from instanceof CountryDetailView)) 47 | throw new IllegalArgumentException("The from view must be a CountryDetailView"); 48 | 49 | if (to == null) 50 | throw new IllegalArgumentException("The to view must not be null"); 51 | 52 | final CountryDetailView detailView = (CountryDetailView)from; 53 | 54 | AnimatorSet animatorSet = new AnimatorSet(); 55 | 56 | // Set the to View's alpha to 0 to hide it at the beginning. 57 | to.setAlpha(0); 58 | 59 | // Scale down to hide the fab button 60 | PropertyValuesHolder fabScaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 0); 61 | PropertyValuesHolder fabScaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0); 62 | Animator hideFabButtonAnimator = ObjectAnimator.ofPropertyValuesHolder(detailView.favouriteFab, fabScaleX, fabScaleY); 63 | 64 | // Slide up the flag 65 | Animator flagAnimator = ObjectAnimator.ofFloat(detailView.flagView, View.TRANSLATION_Y, 0, -detailView.flagView.getHeight()); 66 | 67 | // Slide down the details 68 | Animator detailAnimator = ObjectAnimator.ofFloat(detailView.detailGroup, View.TRANSLATION_Y, 0, detailView.detailGroup.getHeight()); 69 | 70 | // Show the new view 71 | Animator showToViewAnimator = ObjectAnimator.ofFloat(to, View.ALPHA, 0, 1); 72 | 73 | animatorSet.playTogether(hideFabButtonAnimator, flagAnimator, detailAnimator, showToViewAnimator); 74 | animatorSet.setDuration(300); 75 | animatorSet.setInterpolator(new FastOutLinearInInterpolator()); 76 | 77 | animatorSet.start(); 78 | 79 | return animatorSet; 80 | } 81 | 82 | @Override 83 | protected void resetFromView(@NonNull View from) { 84 | CountryDetailView detailView = (CountryDetailView) from; 85 | detailView.favouriteFab.setScaleX(1); 86 | detailView.favouriteFab.setScaleY(1); 87 | detailView.flagView.setTranslationY(0); 88 | detailView.detailGroup.setTranslationY(0); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/transition/DetailPopTransChangeHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.transition; 20 | 21 | import android.annotation.TargetApi; 22 | import android.os.Build; 23 | import android.os.Bundle; 24 | import android.support.annotation.NonNull; 25 | import android.support.annotation.Nullable; 26 | import android.transition.ChangeBounds; 27 | import android.transition.ChangeClipBounds; 28 | import android.transition.ChangeImageTransform; 29 | import android.transition.ChangeTransform; 30 | import android.transition.Slide; 31 | import android.transition.Transition; 32 | import android.transition.TransitionSet; 33 | import android.view.View; 34 | import android.view.ViewGroup; 35 | 36 | import com.bluelinelabs.conductor.changehandler.TransitionChangeHandler; 37 | 38 | import net.huannguyen.conductorexample.countrydetail.CountryDetailView; 39 | 40 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 41 | public class DetailPopTransChangeHandler extends TransitionChangeHandler { 42 | 43 | private static final String KEY_FLAG_TRANSITION_NAME = "key_flag_transition_name"; 44 | 45 | private String flagViewTransitionName; 46 | 47 | public DetailPopTransChangeHandler() { 48 | this.flagViewTransitionName = null; 49 | } 50 | 51 | public DetailPopTransChangeHandler(String flagViewTransitionName) { 52 | this.flagViewTransitionName = flagViewTransitionName; 53 | } 54 | 55 | @Override 56 | public void saveToBundle(@NonNull Bundle bundle) { 57 | bundle.putString(KEY_FLAG_TRANSITION_NAME, flagViewTransitionName); 58 | } 59 | 60 | @Override 61 | public void restoreFromBundle(@NonNull Bundle bundle) { 62 | flagViewTransitionName = bundle.getString(KEY_FLAG_TRANSITION_NAME); 63 | } 64 | 65 | @NonNull 66 | @Override 67 | protected Transition getTransition(@NonNull ViewGroup container, 68 | @Nullable View from, 69 | @Nullable View to, 70 | boolean isPush) { 71 | if (from == null || !(from instanceof CountryDetailView)) 72 | throw new IllegalArgumentException("The from view must be a CountryDetailView"); 73 | 74 | CountryDetailView detailView = (CountryDetailView) from; 75 | detailView.flagView.setTransitionName(flagViewTransitionName); 76 | 77 | return new TransitionSet() 78 | .addTransition(new TransitionSet() 79 | .addTransition(new ChangeBounds()) 80 | .addTransition(new ChangeClipBounds()) 81 | .addTransition(new ChangeTransform()) 82 | .addTransition(new ChangeImageTransform())) 83 | .addTransition(new Slide().addTarget(detailView.detailGroup)) 84 | .addTransition(new Scale().addTarget(detailView.favouriteFab)); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/transition/DetailPushAnimChangeHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.transition; 20 | 21 | import android.animation.Animator; 22 | import android.animation.AnimatorSet; 23 | import android.animation.ObjectAnimator; 24 | import android.animation.PropertyValuesHolder; 25 | import android.support.annotation.NonNull; 26 | import android.support.annotation.Nullable; 27 | import android.support.v4.view.animation.FastOutSlowInInterpolator; 28 | import android.view.View; 29 | import android.view.ViewGroup; 30 | 31 | import com.bluelinelabs.conductor.changehandler.AnimatorChangeHandler; 32 | 33 | import net.huannguyen.conductorexample.countrydetail.CountryDetailView; 34 | 35 | public class DetailPushAnimChangeHandler extends AnimatorChangeHandler { 36 | 37 | @NonNull 38 | @Override 39 | protected Animator getAnimator(@NonNull ViewGroup container, 40 | @Nullable View from, 41 | @Nullable View to, 42 | boolean isPush, 43 | boolean toAddedToContainer) { 44 | 45 | // Make sure the to view is a CountryDetailView 46 | if (to == null || !(to instanceof CountryDetailView)) 47 | throw new IllegalArgumentException("The to view must be a CountryDetailView"); 48 | 49 | final CountryDetailView detailView = (CountryDetailView)to; 50 | 51 | // Set the button scale to 0 to make it invisible at the beginning. 52 | detailView.favouriteFab.setScaleX(0); 53 | detailView.favouriteFab.setScaleY(0); 54 | 55 | AnimatorSet animatorSet = new AnimatorSet(); 56 | 57 | AnimatorSet flagAndDetailAnim = new AnimatorSet(); 58 | 59 | // Hide the old view 60 | Animator hideFromViewAnim = ObjectAnimator.ofFloat(from, View.ALPHA, 1, 0); 61 | 62 | // Slide down the flag 63 | Animator flagAnim = ObjectAnimator.ofFloat(detailView.flagView, View.TRANSLATION_Y, -detailView.flagView.getHeight(), 0); 64 | 65 | // Slide up the details 66 | Animator detailAnim = ObjectAnimator.ofFloat(detailView.detailGroup, View.TRANSLATION_Y, detailView.detailGroup.getHeight(), 0); 67 | 68 | flagAndDetailAnim.playTogether(hideFromViewAnim, flagAnim, detailAnim); 69 | flagAndDetailAnim.setDuration(300); 70 | flagAndDetailAnim.setInterpolator(new FastOutSlowInInterpolator()); 71 | 72 | // Scale up the favourite fab 73 | PropertyValuesHolder fabScaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 0, 1); 74 | PropertyValuesHolder fabScaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0, 1); 75 | Animator favouriteAnim = ObjectAnimator.ofPropertyValuesHolder(detailView.favouriteFab, fabScaleX, fabScaleY) 76 | .setDuration(200); 77 | 78 | animatorSet.playSequentially(flagAndDetailAnim, favouriteAnim); 79 | 80 | animatorSet.start(); 81 | 82 | return animatorSet; 83 | } 84 | 85 | @Override 86 | protected void resetFromView(@NonNull View from) { 87 | from.setAlpha(1); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/transition/DetailPushTransChangeHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.transition; 20 | 21 | import android.annotation.TargetApi; 22 | import android.os.Build; 23 | import android.os.Bundle; 24 | import android.support.annotation.NonNull; 25 | import android.support.annotation.Nullable; 26 | import android.support.v4.view.animation.FastOutSlowInInterpolator; 27 | import android.transition.ChangeBounds; 28 | import android.transition.ChangeClipBounds; 29 | import android.transition.ChangeImageTransform; 30 | import android.transition.ChangeTransform; 31 | import android.transition.Slide; 32 | import android.transition.Transition; 33 | import android.transition.TransitionSet; 34 | import android.view.View; 35 | import android.view.ViewGroup; 36 | 37 | import com.bluelinelabs.conductor.changehandler.TransitionChangeHandler; 38 | 39 | import net.huannguyen.conductorexample.countrydetail.CountryDetailView; 40 | 41 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 42 | public class DetailPushTransChangeHandler extends TransitionChangeHandler { 43 | 44 | private static final String KEY_FLAG_TRANSITION_NAME = "key_flag_transition_name"; 45 | 46 | private String flagViewTransitionName; 47 | 48 | public DetailPushTransChangeHandler() {} 49 | 50 | public DetailPushTransChangeHandler(String flagViewTransitionName) { 51 | this.flagViewTransitionName = flagViewTransitionName; 52 | } 53 | 54 | @Override 55 | public void saveToBundle(@NonNull Bundle bundle) { 56 | bundle.putString(KEY_FLAG_TRANSITION_NAME, flagViewTransitionName); 57 | } 58 | 59 | @Override 60 | public void restoreFromBundle(@NonNull Bundle bundle) { 61 | flagViewTransitionName = bundle.getString(KEY_FLAG_TRANSITION_NAME); 62 | } 63 | 64 | @NonNull 65 | @Override 66 | protected Transition getTransition(@NonNull ViewGroup container, 67 | @Nullable View from, 68 | @Nullable View to, 69 | boolean isPush) { 70 | 71 | if (to == null || !(to instanceof CountryDetailView)) { 72 | throw new IllegalArgumentException("The to view must be a CountryDetailView"); 73 | } 74 | 75 | final CountryDetailView detailView = (CountryDetailView) to; 76 | 77 | detailView.flagView.setTransitionName(flagViewTransitionName); 78 | 79 | ChangeTransform changeTransform = new ChangeTransform(); 80 | 81 | // Shared elements (the flag view in this case) are drawn in the window's view overlay during the transition by default. 82 | // That causes the favourite fab being drawn behind the flag when it is scaled up. 83 | // Setting the change transform not using overlay addresses this issue. 84 | changeTransform.setReparentWithOverlay(false); 85 | 86 | return new TransitionSet() 87 | .addTransition(new TransitionSet() 88 | .addTransition(new ChangeBounds()) 89 | .addTransition(new ChangeClipBounds()) 90 | .addTransition(changeTransform) 91 | .addTransition(new ChangeImageTransform()) 92 | .setDuration(300)) 93 | .addTransition(new Slide().addTarget(detailView.detailGroup).setStartDelay(150)) 94 | .addTransition(new Scale().addTarget(detailView.favouriteFab).setStartDelay(300)) 95 | .setInterpolator(new FastOutSlowInInterpolator()); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/java/net/huannguyen/conductorexample/transition/Scale.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright (C) 2017 Huan Nguyen. 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * 17 | */ 18 | 19 | package net.huannguyen.conductorexample.transition; 20 | 21 | import android.animation.Animator; 22 | import android.animation.ObjectAnimator; 23 | import android.animation.PropertyValuesHolder; 24 | import android.annotation.TargetApi; 25 | import android.content.Context; 26 | import android.os.Build; 27 | import android.transition.TransitionValues; 28 | import android.transition.Visibility; 29 | import android.util.AttributeSet; 30 | import android.view.View; 31 | import android.view.ViewGroup; 32 | 33 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 34 | public class Scale extends Visibility { 35 | 36 | public Scale() {} 37 | 38 | public Scale(Context context, AttributeSet attrs) { 39 | super(context, attrs); 40 | } 41 | 42 | @Override 43 | public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { 44 | return getAnimator(view, 0, 1); 45 | } 46 | 47 | @Override 48 | public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { 49 | return getAnimator(view, 1, 0); 50 | } 51 | 52 | private Animator getAnimator(View view, float startValue, float endValue) { 53 | view.setScaleX(startValue); 54 | view.setScaleY(startValue); 55 | PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, startValue, endValue); 56 | PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, startValue, endValue); 57 | return ObjectAnimator.ofPropertyValuesHolder(view, scaleX, scaleY); 58 | } 59 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_favorite.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ripple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout-land/country_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 17 | 18 | 22 | 23 | 31 | 32 | 43 | 44 | 45 | 52 | 53 | 54 | 59 | 60 | 65 | 66 | 67 | 68 | 73 | 74 | 79 | 80 | 81 | 82 | 87 | 88 | 93 | 94 | 95 | 96 | 101 | 102 | 107 | 108 | 109 | 110 | 115 | 116 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 15 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/country_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | 24 | 25 | 31 | 32 | 33 | 38 | 39 | 44 | 45 | 46 | 47 | 52 | 53 | 58 | 59 | 60 | 61 | 66 | 67 | 72 | 73 | 74 | 75 | 80 | 81 | 86 | 87 | 88 | 89 | 94 | 95 | 100 | 101 | 102 | 103 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /app/src/main/res/layout/country_grid.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/country_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-nguyen/ConductorExample/0e7029402f3179130967f3e23f2abaed78c861a0/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-nguyen/ConductorExample/0e7029402f3179130967f3e23f2abaed78c861a0/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-nguyen/ConductorExample/0e7029402f3179130967f3e23f2abaed78c861a0/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-nguyen/ConductorExample/0e7029402f3179130967f3e23f2abaed78c861a0/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-nguyen/ConductorExample/0e7029402f3179130967f3e23f2abaed78c861a0/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-nguyen/ConductorExample/0e7029402f3179130967f3e23f2abaed78c861a0/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-nguyen/ConductorExample/0e7029402f3179130967f3e23f2abaed78c861a0/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-nguyen/ConductorExample/0e7029402f3179130967f3e23f2abaed78c861a0/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-nguyen/ConductorExample/0e7029402f3179130967f3e23f2abaed78c861a0/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-nguyen/ConductorExample/0e7029402f3179130967f3e23f2abaed78c861a0/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-v21/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -28dp 4 | 28dp 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #424242 4 | #212121 5 | #B2FF59 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -44dp 6 | 12dp 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Conductor Example 3 | Capital 4 | Population 5 | Countries 6 | Language 7 | Currency 8 | Timezone 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 16 | 17 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /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 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /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 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-nguyen/ConductorExample/0e7029402f3179130967f3e23f2abaed78c861a0/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Apr 14 20:42:54 AEST 2017 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-3.3-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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------