├── .gitignore ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── shvartsy │ │ └── simpleviewmodel │ │ └── simpleviewmodel │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── shvartsy │ │ │ └── archcomponents │ │ │ ├── view │ │ │ ├── LiveDataDemoActivity.java │ │ │ ├── LoggingViewModelDemoActivity.java │ │ │ └── ViewModelDemoActivity.java │ │ │ └── viewmodel │ │ │ ├── ClickCounterViewModel.java │ │ │ ├── ClickLoggingInterceptor.java │ │ │ ├── LiveDataTimerViewModel.java │ │ │ ├── LoggingClickCounterViewModel.java │ │ │ ├── LoggingClickCounterViewModelFactory.java │ │ │ └── MyObserver.java │ └── res │ │ ├── layout │ │ ├── activity_livedata_demo.xml │ │ └── activity_viewmodel_demo.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 │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── shvartsy │ └── simpleviewmodel │ └── simpleviewmodel │ └── ExampleUnitTest.java ├── 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 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.shvartsy.simpleviewmodel.simpleviewmodel" 7 | minSdkVersion 19 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { 24 | exclude group: 'com.android.support', module: 'support-annotations' 25 | }) 26 | implementation 'com.android.support:appcompat-v7:27.1.0' 27 | testImplementation 'junit:junit:4.12' 28 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 29 | 30 | // Lifecycle 31 | implementation "android.arch.lifecycle:runtime:1.1.1" 32 | implementation "android.arch.lifecycle:extensions:1.1.1" 33 | annotationProcessor "android.arch.lifecycle:compiler:1.1.1" 34 | 35 | // Butterknife 36 | implementation 'com.jakewharton:butterknife:8.6.0' 37 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.6.0' 38 | } 39 | -------------------------------------------------------------------------------- /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/shvartsy/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/androidTest/java/com/shvartsy/simpleviewmodel/simpleviewmodel/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.shvartsy.simpleviewmodel.simpleviewmodel; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.shvartsy.simpleviewmodel.simpleviewmodel", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/shvartsy/archcomponents/view/LiveDataDemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.shvartsy.archcomponents.view; 2 | 3 | import android.arch.lifecycle.Observer; 4 | import android.arch.lifecycle.ViewModelProviders; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.util.Log; 9 | import android.widget.TextView; 10 | 11 | import com.shvartsy.archcomponents.R; 12 | import com.shvartsy.archcomponents.viewmodel.LiveDataTimerViewModel; 13 | import com.shvartsy.archcomponents.viewmodel.MyObserver; 14 | 15 | import butterknife.BindView; 16 | import butterknife.ButterKnife; 17 | 18 | public class LiveDataDemoActivity extends AppCompatActivity { 19 | 20 | private static final String LOG_TAG = LiveDataDemoActivity.class.getSimpleName(); 21 | 22 | private LiveDataTimerViewModel liveDataTimerViewModel; 23 | 24 | private final Observer elapsedTimeObserver = new Observer() { 25 | @Override 26 | public void onChanged(@Nullable final Long newValue) { 27 | String newText = LiveDataDemoActivity.this.getResources().getString(R.string.seconds, newValue); 28 | displayTimerValue(newText); 29 | Log.d(LOG_TAG, "Updating timer"); 30 | } 31 | }; 32 | 33 | @BindView(R.id.timer_value_text) 34 | protected TextView timerValueText; 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_livedata_demo); 40 | ButterKnife.bind(this); 41 | 42 | liveDataTimerViewModel = ViewModelProviders.of(this).get(LiveDataTimerViewModel.class); 43 | 44 | subscribeElapsedTimeObserver(); 45 | 46 | getLifecycle().addObserver(new MyObserver()); 47 | } 48 | 49 | private void subscribeElapsedTimeObserver() { 50 | liveDataTimerViewModel.getElapsedTime().observe(this, elapsedTimeObserver); 51 | } 52 | 53 | private void displayTimerValue(String value) { 54 | timerValueText.setText(String.valueOf(value)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/shvartsy/archcomponents/view/LoggingViewModelDemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.shvartsy.archcomponents.view; 2 | 3 | import android.arch.lifecycle.ViewModelProviders; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.TextView; 8 | 9 | import com.shvartsy.archcomponents.R; 10 | import com.shvartsy.archcomponents.viewmodel.ClickCounterViewModel; 11 | import com.shvartsy.archcomponents.viewmodel.ClickLoggingInterceptor; 12 | import com.shvartsy.archcomponents.viewmodel.LoggingClickCounterViewModel; 13 | import com.shvartsy.archcomponents.viewmodel.LoggingClickCounterViewModelFactory; 14 | 15 | import butterknife.BindView; 16 | import butterknife.ButterKnife; 17 | import butterknife.OnClick; 18 | 19 | public class LoggingViewModelDemoActivity extends AppCompatActivity { 20 | 21 | @BindView(R.id.click_count_text) 22 | protected TextView clickCountText; 23 | 24 | private ClickCounterViewModel viewModel; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_viewmodel_demo); 30 | ButterKnife.bind(this); 31 | 32 | // the factory and its dependencies instead should be injected with DI framework like Dagger 33 | LoggingClickCounterViewModelFactory factory = 34 | new LoggingClickCounterViewModelFactory(new ClickLoggingInterceptor()); 35 | 36 | viewModel = ViewModelProviders.of(this, factory).get(LoggingClickCounterViewModel.class); 37 | displayClickCount(viewModel.getCount()); 38 | } 39 | 40 | @OnClick(R.id.increment_button) 41 | public void incrementClickCount(View button) { 42 | viewModel.setCount(viewModel.getCount() + 1); 43 | displayClickCount(viewModel.getCount()); 44 | } 45 | 46 | private void displayClickCount(int clickCount) { 47 | clickCountText.setText(String.valueOf(clickCount)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/shvartsy/archcomponents/view/ViewModelDemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.shvartsy.archcomponents.view; 2 | 3 | import android.arch.lifecycle.ViewModelProviders; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.TextView; 8 | 9 | import com.shvartsy.archcomponents.R; 10 | import com.shvartsy.archcomponents.viewmodel.ClickCounterViewModel; 11 | 12 | import butterknife.BindView; 13 | import butterknife.ButterKnife; 14 | import butterknife.OnClick; 15 | 16 | public class ViewModelDemoActivity extends AppCompatActivity { 17 | 18 | @BindView(R.id.click_count_text) 19 | protected TextView clickCountText; 20 | 21 | private ClickCounterViewModel viewModel; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_viewmodel_demo); 27 | ButterKnife.bind(this); 28 | 29 | viewModel = ViewModelProviders.of(this).get(ClickCounterViewModel.class); 30 | displayClickCount(viewModel.getCount()); 31 | } 32 | 33 | @OnClick(R.id.increment_button) 34 | public void incrementClickCount(View button) { 35 | viewModel.setCount(viewModel.getCount() + 1); 36 | displayClickCount(viewModel.getCount()); 37 | } 38 | 39 | private void displayClickCount(int clickCount) { 40 | clickCountText.setText(String.valueOf(clickCount)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/shvartsy/archcomponents/viewmodel/ClickCounterViewModel.java: -------------------------------------------------------------------------------- 1 | package com.shvartsy.archcomponents.viewmodel; 2 | 3 | import android.arch.lifecycle.ViewModel; 4 | 5 | /** 6 | * Basic view model implementation to illustrate the ViewModel functionality. 7 | */ 8 | 9 | public class ClickCounterViewModel extends ViewModel { 10 | private int count; 11 | 12 | public void setCount(int count) { 13 | this.count = count; 14 | } 15 | 16 | public int getCount() { 17 | return count; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/shvartsy/archcomponents/viewmodel/ClickLoggingInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.shvartsy.archcomponents.viewmodel; 2 | 3 | import android.util.Log; 4 | 5 | public class ClickLoggingInterceptor { 6 | private static final String LOG_TAG = ClickLoggingInterceptor.class.getSimpleName(); 7 | 8 | public void intercept(int clickCount) { 9 | Log.d(LOG_TAG, "processed click " + clickCount); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/shvartsy/archcomponents/viewmodel/LiveDataTimerViewModel.java: -------------------------------------------------------------------------------- 1 | package com.shvartsy.archcomponents.viewmodel; 2 | 3 | import android.arch.lifecycle.LiveData; 4 | import android.arch.lifecycle.MutableLiveData; 5 | import android.arch.lifecycle.ViewModel; 6 | import android.os.Handler; 7 | import android.os.Looper; 8 | import android.os.SystemClock; 9 | 10 | import java.util.Timer; 11 | import java.util.TimerTask; 12 | 13 | /** 14 | * Demonstrates LiveData functionality. 15 | */ 16 | 17 | public class LiveDataTimerViewModel extends ViewModel { 18 | private static final int ONE_SECOND = 1000; 19 | 20 | private MutableLiveData elapsedTime = new MutableLiveData<>(); 21 | 22 | private long initialTime; 23 | 24 | public LiveDataTimerViewModel() { 25 | initialTime = SystemClock.elapsedRealtime(); 26 | Timer timer = new Timer(); 27 | 28 | // Update the elapsed time every second. 29 | timer.scheduleAtFixedRate(new TimerTask() { 30 | @Override 31 | public void run() { 32 | final long newValue = (SystemClock.elapsedRealtime() - initialTime) / 1000; 33 | // setValue() cannot be called from a background thread so post to main thread. 34 | new Handler(Looper.getMainLooper()).post(new Runnable() { 35 | @Override 36 | public void run() { 37 | elapsedTime.setValue(newValue); 38 | 39 | } 40 | }); 41 | } 42 | }, ONE_SECOND, ONE_SECOND); 43 | } 44 | 45 | public LiveData getElapsedTime() { 46 | return elapsedTime; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/shvartsy/archcomponents/viewmodel/LoggingClickCounterViewModel.java: -------------------------------------------------------------------------------- 1 | package com.shvartsy.archcomponents.viewmodel; 2 | 3 | /** 4 | * ViewModel implementation that logs every count increment. 5 | */ 6 | public class LoggingClickCounterViewModel extends ClickCounterViewModel { 7 | private final ClickLoggingInterceptor loggingInterceptor; 8 | 9 | public LoggingClickCounterViewModel(ClickLoggingInterceptor loggingInterceptor) { 10 | this.loggingInterceptor = loggingInterceptor; 11 | } 12 | 13 | @Override 14 | public void setCount(int count) { 15 | super.setCount(count); 16 | loggingInterceptor.intercept(count); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/shvartsy/archcomponents/viewmodel/LoggingClickCounterViewModelFactory.java: -------------------------------------------------------------------------------- 1 | package com.shvartsy.archcomponents.viewmodel; 2 | 3 | import android.arch.lifecycle.ViewModel; 4 | import android.arch.lifecycle.ViewModelProvider; 5 | 6 | public class LoggingClickCounterViewModelFactory implements ViewModelProvider.Factory { 7 | 8 | private final ClickLoggingInterceptor loggingInterceptor; 9 | 10 | public LoggingClickCounterViewModelFactory(ClickLoggingInterceptor loggingInterceptor) { 11 | this.loggingInterceptor = loggingInterceptor; 12 | } 13 | 14 | public T create(Class modelClass) { 15 | if (modelClass.isAssignableFrom(LoggingClickCounterViewModel.class)) { 16 | return (T) new LoggingClickCounterViewModel(loggingInterceptor); 17 | } 18 | throw new IllegalArgumentException("Unknown ViewModel class"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/shvartsy/archcomponents/viewmodel/MyObserver.java: -------------------------------------------------------------------------------- 1 | package com.shvartsy.archcomponents.viewmodel; 2 | 3 | import android.arch.lifecycle.Lifecycle; 4 | import android.arch.lifecycle.LifecycleObserver; 5 | import android.arch.lifecycle.OnLifecycleEvent; 6 | import android.util.Log; 7 | 8 | /** 9 | * Demonstrates usage of basic LifecycleObserver. 10 | */ 11 | public class MyObserver implements LifecycleObserver { 12 | private static final String LOG_TAG = MyObserver.class.getSimpleName(); 13 | 14 | @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) 15 | public void onResume() { 16 | Log.d(LOG_TAG, "resumed observing lifecycle."); 17 | } 18 | 19 | @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) 20 | public void onPause() { 21 | Log.d(LOG_TAG, "paused observing lifecycle."); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_livedata_demo.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_viewmodel_demo.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 |