From 02517202cddf1180cd8639a11e8c44a4d04bf53c Mon Sep 17 00:00:00 2001 From: Debdatta Basu Date: Sun, 14 Sep 2014 00:22:24 +0530 Subject: [PATCH] initial commit --- .gitignore | 32 ++ LICENSE.txt | 28 ++ README.md | 196 ++++++++++++ build.gradle | 16 + gradle/wrapper/gradle-wrapper.properties | 6 + library/.gitignore | 1 + library/build.gradle | 30 ++ library/proguard-rules.pro | 17 + library/src/main/AndroidManifest.xml | 4 + .../dbasu/robomvvm/annotation/SetLayout.java | 53 ++++ .../dbasu/robomvvm/binding/ActionBinding.java | 86 +++++ .../org/dbasu/robomvvm/binding/BindMode.java | 80 +++++ .../org/dbasu/robomvvm/binding/Binding.java | 243 +++++++++++++++ .../binding/DefaultValueConverter.java | 47 +++ .../robomvvm/binding/PropertyBinding.java | 151 +++++++++ .../robomvvm/binding/TypedValueConverter.java | 127 ++++++++ .../robomvvm/binding/ValueConverter.java | 61 ++++ .../adapterview/AdapterViewAdapter.java | 128 ++++++++ .../adapterview/ItemSelectEventArg.java | 64 ++++ .../compoundbutton/CheckedChangeEventArg.java | 65 ++++ .../compoundbutton/CheckedEventArg.java | 48 +++ .../CompoundButtonViewAdapter.java | 68 ++++ .../compoundbutton/UncheckedEventArg.java | 48 +++ .../edittext/EditTextViewAdapter.java | 80 +++++ .../edittext/TextChangeEventArg.java | 107 +++++++ .../imageview/ImageViewAdapter.java | 74 +++++ .../listview/ItemClickEventArg.java | 67 ++++ .../listview/ItemLongClickEventArg.java | 65 ++++ .../listview/ListViewAdapter.java | 110 +++++++ .../menuitem/MenuItemAdapter.java | 160 ++++++++++ .../menuitem/MenuItemClickEventArg.java | 50 +++ .../menuitem/MenuItemCollapsedEventArg.java | 50 +++ .../MenuItemExpandedChangeEventArg.java | 64 ++++ .../menuitem/MenuItemExpandedEventArg.java | 50 +++ .../progressbar/ProgressBarViewAdapter.java | 74 +++++ .../ratingbar/RatingBarViewAdapter.java | 79 +++++ .../ratingbar/RatingChangeEventArg.java | 78 +++++ .../seekbar/ProgressChangeEventArg.java | 80 +++++ .../seekbar/SeekBarViewAdapter.java | 77 +++++ .../textview/TextViewAdapter.java | 62 ++++ .../componentadapter/view/ClickEventArg.java | 50 +++ .../view/FocusChangeEventArg.java | 65 ++++ .../componentadapter/view/FocusEventArg.java | 48 +++ .../view/FocusLostEventArg.java | 49 +++ .../view/LongClickEventArg.java | 51 +++ .../componentadapter/view/ViewAdapter.java | 139 +++++++++ .../componentmodel/ActionManager.java | 176 +++++++++++ .../robomvvm/componentmodel/Component.java | 229 ++++++++++++++ .../componentmodel/ComponentAdapter.java | 229 ++++++++++++++ .../robomvvm/componentmodel/EventArg.java | 61 ++++ .../componentmodel/EventListener.java | 73 +++++ .../GarbageCollectionEventArg.java | 47 +++ .../PropertyChangeEventArg.java | 61 ++++ .../componentmodel/PropertyManager.java | 205 ++++++++++++ .../org/dbasu/robomvvm/util/ObjectTagger.java | 130 ++++++++ .../org/dbasu/robomvvm/util/ThreadUtil.java | 49 +++ .../robomvvm/viewmodel/BaseViewModel.java | 168 ++++++++++ .../viewmodel/ItemCheckedEventArg.java | 78 +++++ .../robomvvm/viewmodel/MenuViewModel.java | 172 ++++++++++ .../dbasu/robomvvm/viewmodel/ViewModel.java | 205 ++++++++++++ .../viewmodel/ViewModelCollection.java | 293 ++++++++++++++++++ sample_itemlist/.gitignore | 1 + sample_itemlist/build.gradle | 25 ++ sample_itemlist/proguard-rules.pro | 17 + sample_itemlist/src/main/AndroidManifest.xml | 21 ++ .../sample_itemlist/AboutPopupViewModel.java | 59 ++++ .../sample_itemlist/MainActivity.java | 93 ++++++ .../MainActivityViewModel.java | 148 +++++++++ .../sample_itemlist/MainMenuViewModel.java | 63 ++++ .../sample_itemlist/StringViewModel.java | 65 ++++ .../main/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 9397 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 5237 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 14383 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 19388 bytes .../src/main/res/drawable/drop_shadow.xml | 13 + .../src/main/res/layout/activity_main.xml | 73 +++++ .../src/main/res/layout/popup_about.xml | 24 ++ .../src/main/res/layout/string_view.xml | 16 + sample_itemlist/src/main/res/menu/main.xml | 8 + .../src/main/res/values-w820dp/dimens.xml | 6 + .../src/main/res/values/dimens.xml | 5 + .../src/main/res/values/strings.xml | 13 + .../src/main/res/values/styles.xml | 8 + sample_textsync/.gitignore | 1 + sample_textsync/build.gradle | 25 ++ sample_textsync/proguard-rules.pro | 17 + sample_textsync/src/main/AndroidManifest.xml | 21 ++ sample_textsync/src/main/ic_launcher-web.png | Bin 0 -> 47065 bytes .../sample_textsync/MainActivity.java | 46 +++ .../main/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 7721 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 3769 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 12329 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 24654 bytes .../src/main/res/layout/activity_main.xml | 24 ++ sample_textsync/src/main/res/menu/main.xml | 8 + .../src/main/res/values-w820dp/dimens.xml | 6 + .../src/main/res/values/dimens.xml | 5 + .../src/main/res/values/strings.xml | 8 + .../src/main/res/values/styles.xml | 8 + sample_textsync_no_mvvm/.gitignore | 1 + sample_textsync_no_mvvm/build.gradle | 24 ++ sample_textsync_no_mvvm/proguard-rules.pro | 17 + .../src/main/AndroidManifest.xml | 21 ++ .../src/main/ic_launcher-web.png | Bin 0 -> 47065 bytes .../sample_textsync_no_mvvm/MainActivity.java | 66 ++++ .../main/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 7721 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 3769 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 12329 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 24654 bytes .../src/main/res/layout/activity_main.xml | 24 ++ .../src/main/res/menu/main.xml | 8 + .../src/main/res/values-w820dp/dimens.xml | 6 + .../src/main/res/values/dimens.xml | 5 + .../src/main/res/values/strings.xml | 8 + .../src/main/res/values/styles.xml | 8 + settings.gradle | 1 + 116 files changed, 6520 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 library/.gitignore create mode 100644 library/build.gradle create mode 100644 library/proguard-rules.pro create mode 100644 library/src/main/AndroidManifest.xml create mode 100644 library/src/main/java/org/dbasu/robomvvm/annotation/SetLayout.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/binding/ActionBinding.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/binding/BindMode.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/binding/Binding.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/binding/DefaultValueConverter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/binding/PropertyBinding.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/binding/TypedValueConverter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/binding/ValueConverter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/adapterview/AdapterViewAdapter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/adapterview/ItemSelectEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/CheckedChangeEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/CheckedEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/CompoundButtonViewAdapter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/UncheckedEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/edittext/EditTextViewAdapter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/edittext/TextChangeEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/imageview/ImageViewAdapter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/listview/ItemClickEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/listview/ItemLongClickEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/listview/ListViewAdapter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemAdapter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemClickEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemCollapsedEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemExpandedChangeEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemExpandedEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/progressbar/ProgressBarViewAdapter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/ratingbar/RatingBarViewAdapter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/ratingbar/RatingChangeEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/seekbar/ProgressChangeEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/seekbar/SeekBarViewAdapter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/textview/TextViewAdapter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/view/ClickEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/view/FocusChangeEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/view/FocusEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/view/FocusLostEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/view/LongClickEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentadapter/view/ViewAdapter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentmodel/ActionManager.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentmodel/Component.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentmodel/ComponentAdapter.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentmodel/EventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentmodel/EventListener.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentmodel/GarbageCollectionEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentmodel/PropertyChangeEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/componentmodel/PropertyManager.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/util/ObjectTagger.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/util/ThreadUtil.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/viewmodel/BaseViewModel.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/viewmodel/ItemCheckedEventArg.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/viewmodel/MenuViewModel.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/viewmodel/ViewModel.java create mode 100644 library/src/main/java/org/dbasu/robomvvm/viewmodel/ViewModelCollection.java create mode 100644 sample_itemlist/.gitignore create mode 100644 sample_itemlist/build.gradle create mode 100644 sample_itemlist/proguard-rules.pro create mode 100644 sample_itemlist/src/main/AndroidManifest.xml create mode 100644 sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/AboutPopupViewModel.java create mode 100644 sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/MainActivity.java create mode 100644 sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/MainActivityViewModel.java create mode 100644 sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/MainMenuViewModel.java create mode 100644 sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/StringViewModel.java create mode 100644 sample_itemlist/src/main/res/drawable-hdpi/ic_launcher.png create mode 100644 sample_itemlist/src/main/res/drawable-mdpi/ic_launcher.png create mode 100644 sample_itemlist/src/main/res/drawable-xhdpi/ic_launcher.png create mode 100644 sample_itemlist/src/main/res/drawable-xxhdpi/ic_launcher.png create mode 100644 sample_itemlist/src/main/res/drawable/drop_shadow.xml create mode 100644 sample_itemlist/src/main/res/layout/activity_main.xml create mode 100644 sample_itemlist/src/main/res/layout/popup_about.xml create mode 100644 sample_itemlist/src/main/res/layout/string_view.xml create mode 100644 sample_itemlist/src/main/res/menu/main.xml create mode 100644 sample_itemlist/src/main/res/values-w820dp/dimens.xml create mode 100644 sample_itemlist/src/main/res/values/dimens.xml create mode 100644 sample_itemlist/src/main/res/values/strings.xml create mode 100644 sample_itemlist/src/main/res/values/styles.xml create mode 100644 sample_textsync/.gitignore create mode 100644 sample_textsync/build.gradle create mode 100644 sample_textsync/proguard-rules.pro create mode 100644 sample_textsync/src/main/AndroidManifest.xml create mode 100644 sample_textsync/src/main/ic_launcher-web.png create mode 100644 sample_textsync/src/main/java/org/dbasu/robomvvm/sample_textsync/MainActivity.java create mode 100644 sample_textsync/src/main/res/drawable-hdpi/ic_launcher.png create mode 100644 sample_textsync/src/main/res/drawable-mdpi/ic_launcher.png create mode 100644 sample_textsync/src/main/res/drawable-xhdpi/ic_launcher.png create mode 100644 sample_textsync/src/main/res/drawable-xxhdpi/ic_launcher.png create mode 100644 sample_textsync/src/main/res/layout/activity_main.xml create mode 100644 sample_textsync/src/main/res/menu/main.xml create mode 100644 sample_textsync/src/main/res/values-w820dp/dimens.xml create mode 100644 sample_textsync/src/main/res/values/dimens.xml create mode 100644 sample_textsync/src/main/res/values/strings.xml create mode 100644 sample_textsync/src/main/res/values/styles.xml create mode 100644 sample_textsync_no_mvvm/.gitignore create mode 100644 sample_textsync_no_mvvm/build.gradle create mode 100644 sample_textsync_no_mvvm/proguard-rules.pro create mode 100644 sample_textsync_no_mvvm/src/main/AndroidManifest.xml create mode 100644 sample_textsync_no_mvvm/src/main/ic_launcher-web.png create mode 100644 sample_textsync_no_mvvm/src/main/java/org/dbasu/robomvvm/sample_textsync_no_mvvm/MainActivity.java create mode 100644 sample_textsync_no_mvvm/src/main/res/drawable-hdpi/ic_launcher.png create mode 100644 sample_textsync_no_mvvm/src/main/res/drawable-mdpi/ic_launcher.png create mode 100644 sample_textsync_no_mvvm/src/main/res/drawable-xhdpi/ic_launcher.png create mode 100644 sample_textsync_no_mvvm/src/main/res/drawable-xxhdpi/ic_launcher.png create mode 100644 sample_textsync_no_mvvm/src/main/res/layout/activity_main.xml create mode 100644 sample_textsync_no_mvvm/src/main/res/menu/main.xml create mode 100644 sample_textsync_no_mvvm/src/main/res/values-w820dp/dimens.xml create mode 100644 sample_textsync_no_mvvm/src/main/res/values/dimens.xml create mode 100644 sample_textsync_no_mvvm/src/main/res/values/strings.xml create mode 100644 sample_textsync_no_mvvm/src/main/res/values/styles.xml create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ad95a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +#built application files +*.apk +*.ap_ + +# files for the dex VM +*.dex + +# Java class files +*.class + +# generated files +bin/ +gen/ + +# Local configuration file (sdk path, etc) +local.properties + +# Windows thumbnail db +Thumbs.db + +# OSX files +.DS_Store + + +# Android Studio +.idea +.gradle +*.iml +gradlew +gradlew.bat +gradle.properties +build/ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..059a77b --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,28 @@ +Project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) +Copyright (c) 2014, Debdatta Basu +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may be used + to endorse or promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b0e203d --- /dev/null +++ b/README.md @@ -0,0 +1,196 @@ +RoboMVVM - MVVM Framework For Android +===================================== + +RoboMVVM is an open source library that facilitates the use of the [MVVM](http://en.wikipedia.org/wiki/Model_View_ViewModel) pattern in Android apps. Those familiar with the .NET world can appreciate the ability of the MVVM pattern to simplify building, testing and refactoring UI heavy applications. The MVVM pattern heavily utilizes [Data Binding](http://en.wikipedia.org/wiki/Data_binding), the ability to make property changes in one component reflect in its observers. This requires a mechanism to notify observers of property changes in a component. The MVVM pattern also allows Action Binding, which is the binding of events in a component to actions in its observers. + + +RoboMVVM will save countless hours of your time by providing you with the tools you need to quickly setup bindings between your views and your data models. At the core of the RoboMVVM library is the Component class, which is a container for events, properties, and actions. The Binding class is used to bind properties to properties and events to actions between a source component and a target component. Bindings can be one way or two way as specified by a BindMode, and a ValueConverter is used to convert between source and target properties + +Because Android does not have a standard property change and event notification system, an MVVM library must wrap Android classes in its own adapters to allow for Data and Action binding. RoboMVVM provides the ComponentAdapter base class for this purpose. It also provides many ComponentAdapter subclasses that act as adapters for most commonly used Android classes. + +Please refer to the [Javadoc](http://debdattabasu.github.io/RoboMVVM/javadoc/) for more information. + + +TextSync - An Enlightening Use Case +=================================== + +Android code is notoriously ugly to write and maintain. For example, consider the code we need to write to keep the text property of two [EditText](http://developer.android.com/reference/android/widget/EditText.html)s in sync. You can also find this sample in the repository[here](). + +```java +public class MainActivity extends Activity { + + EditText text0, text1; + + private final TextWatcher watcher0 = new TextWatcher() { + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + text1.removeTextChangedListener(watcher1); + text1.setText(text0.getText().toString()); + text1.addTextChangedListener(watcher1); + } + + @Override + public void afterTextChanged(Editable s) { + + } + }; + + private final TextWatcher watcher1 = new TextWatcher() { + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + text0.removeTextChangedListener(watcher0); + text0.setText(text1.getText().toString()); + text0.addTextChangedListener(watcher0); + } + + @Override + public void afterTextChanged(Editable s) { + + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + text0 = (EditText) findViewById(R.id.edit_text_0); + text1 = (EditText) findViewById(R.id.edit_text_1); + + text0.addTextChangedListener(watcher0); + text1.addTextChangedListener(watcher1); + } +} +``` + +That's an awful lot of code for such a simple task. You will notice that the actual business logic is only a handful of lines. The rest of the code is just boilerplate to conform to how android works. This is repetitive code that must be written a countess number times in every android project, and in many cases, this obfuscates the real intent of the code. + +Now consider the same example with data binding in RoboMVVM. You can also find this sample in the repository[here](). + +```java +public class MainActivity extends Activity { + + @SetLayout(R.layout.activity_main) + public static class MainActivityViewModel extends ViewModel { + + public MainActivityViewModel(Context context) { + super(context); + } + + private String text = "Hello World!"; + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + raisePropertyChangeEvent("text"); + } + + @Override + protected void bind() { + bindProperty("text", R.id.edit_text_0, "text", BindMode.BIDIRECTIONAL); + bindProperty("text", R.id.edit_text_1, "text", BindMode.BIDIRECTIONAL); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(new MainActivityViewModel(this).getView()); + } +} +``` + +It is immediately evident that the code is a lot shorter. What is more important, however, is that the code is now almost entirely business logic. The goal of RoboMVVM is to make your code about your actual data models and business logic, and less about managing Android's idiosyncrasies. + + +Property Binding Mechanism +=========================== + +When a one-way binding is set up from a source property to a target property, an EventListener is added to the source component that listens for changes to the specified property. When that property changes, The new value is retreived from the source object using the source component's getProperty function. The value is then passed through the ValueConverter, if any, and the final value is assigned to the target property using the target component's setProperty function. + +The getProperty function calls a getter function with a corresponding name and return type. The setProperty function chooses a setter function in the target component based on the property name and the type of the supplied argument. For example, if the name of the property is foo, and the supplied argument is of type int, then the following function calls are attempted: + +```java +void setFoo(int arg); +int getFoo(); +``` + +The same mechanism is replicated in the other direction for two-way bindings. + + +Action Binding Mechanism +======================== + +When a binding is set up between an event in the source component to an action in the target component, an EventListener is added to the source component that listens for events of the specified type. When such an event is raised, an action is called using the target component's invokeAction function. This function calls all functions in the component whose names match the supplied action name, have a void return type, and have either a single argument of a type compatible with the supplied event arg, or no arguments. + +For example, when an event of type ClickEventArg, which is derived from EventArg, is assigned to an action named doSomething, all of the following functions are called: + +```java +void doSomething(ClickEventArg arg); + +void doSomething(EventArg arg); + +void doSomething(); +``` + + +Binding ViewModel Lists to Adapter Views +========================================= + +RoboMVVM lets you create lists of arbitrary View Models and bind them to [AdapterViews](http://developer.android.com/reference/android/widget/AdapterView.html). This means that you can add items of vastly different look and feel to the same AdapterView. This can be used to easily create rich dynamic item lists. + + +Item List Sample +================ + +This app lets you add, remove and modify string items in a ListView. It also has an options menu where you can view a description of this app. It demonstrates the binding of View Model Collections, as well as handling of menus. + + +License +======= + +This library uses the [3-Clause BSD License](http://opensource.org/licenses/BSD-3-Clause). + + Project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + Copyright (c) 2014, Debdatta Basu + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may be used + to endorse or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..782b676 --- /dev/null +++ b/build.gradle @@ -0,0 +1,16 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:0.12.2' + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1e61d1f --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip diff --git a/library/.gitignore b/library/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/library/.gitignore @@ -0,0 +1 @@ +/build diff --git a/library/build.gradle b/library/build.gradle new file mode 100644 index 0000000..d6856c6 --- /dev/null +++ b/library/build.gradle @@ -0,0 +1,30 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 18 + buildToolsVersion "20.0.0" + + defaultConfig { + + applicationId "org.dbasu.robomvvm" + minSdkVersion 15 + targetSdkVersion 20 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + runProguard false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile "org.apache.commons:commons-lang3:3.3.2" + compile "com.google.guava:guava:17.0" + +} diff --git a/library/proguard-rules.pro b/library/proguard-rules.pro new file mode 100644 index 0000000..eb07fae --- /dev/null +++ b/library/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Program Files (x86)\Android\android-studio\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml new file mode 100644 index 0000000..49c6545 --- /dev/null +++ b/library/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/library/src/main/java/org/dbasu/robomvvm/annotation/SetLayout.java b/library/src/main/java/org/dbasu/robomvvm/annotation/SetLayout.java new file mode 100644 index 0000000..4f8e329 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/annotation/SetLayout.java @@ -0,0 +1,53 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Attribute to set the layout resource id on a {@link org.dbasu.robomvvm.viewmodel.ViewModel} or a menu resource id on a + * {@link org.dbasu.robomvvm.viewmodel.MenuViewModel}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface SetLayout { + + /** + * The resource id to be set. + * @return + * The resource id. + */ + int value(); +} diff --git a/library/src/main/java/org/dbasu/robomvvm/binding/ActionBinding.java b/library/src/main/java/org/dbasu/robomvvm/binding/ActionBinding.java new file mode 100644 index 0000000..d47e58f --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/binding/ActionBinding.java @@ -0,0 +1,86 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.binding; + +import org.dbasu.robomvvm.componentmodel.Component; +import org.dbasu.robomvvm.componentmodel.EventArg; +import org.dbasu.robomvvm.componentmodel.EventListener; + +/** + * Package private class implementing the logic for creating an action binding. + */ +class ActionBinding extends Binding { + + private final Class eventType; + private final String action; + private final EventListener eventListener; + + ActionBinding(Component source, Component target, Class eventType, final String action) { + super(source, target); + + this.eventType = eventType; + this.action = action; + + eventListener = new EventListener(eventType) { + @Override + public void invoke(EventArg args) { + + Component target = getTarget(); + if(target == null) return; + + target.invokeAction(action, args); + } + }; + } + + @Override + protected void bind() { + + Component source = getSource(); + if(source != null) { + source.addEventListener(eventListener); + } + + super.bind(); + } + + @Override + public void unbind() { + + Component source = getSource(); + if(source != null) { + source.removeEventListener(eventListener); + } + + super.unbind(); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/binding/BindMode.java b/library/src/main/java/org/dbasu/robomvvm/binding/BindMode.java new file mode 100644 index 0000000..25ad712 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/binding/BindMode.java @@ -0,0 +1,80 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.binding; + +/** + * Specifies the direction of a {@link org.dbasu.robomvvm.binding.Binding}. + */ +public enum BindMode { + + /** + * Makes a one-way binding where the target property is set whenever the source property changes. + */ + SOURCE_TO_TARGET(true, false), + + /** + * Makes a one-way binding where the source property is set whenever the target property changes. + */ + TARGET_TO_SOURCE(false, true), + + /** + * Makes a two-way binding that keeps the source property and target property in complete sync. + */ + BIDIRECTIONAL(true, true); + + + /** + * Returns whether this bind mode can bind the source property to the target property. + * @return + * Returns true for {@link #SOURCE_TO_TARGET} and {@link #BIDIRECTIONAL}. + */ + public boolean canBindSourceToTarget() { + return sourceToTarget; + } + + /** + * Returns whether this bind mode can bind the target property to the source property. + * @return + * Returns true for {@link #TARGET_TO_SOURCE} and {@link #BIDIRECTIONAL}. + */ + public boolean canBindTargetToSource() { + return targetToSource; + } + + private boolean sourceToTarget, targetToSource; + + BindMode(boolean sourceToTarget, boolean targetToSource) { + this.sourceToTarget = sourceToTarget; + this.targetToSource = targetToSource; + + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/binding/Binding.java b/library/src/main/java/org/dbasu/robomvvm/binding/Binding.java new file mode 100644 index 0000000..e7ef0ba --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/binding/Binding.java @@ -0,0 +1,243 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.binding; + +import com.google.common.base.Preconditions; + +import org.dbasu.robomvvm.componentmodel.Component; +import org.dbasu.robomvvm.componentmodel.GarbageCollectionEventArg; +import org.dbasu.robomvvm.componentmodel.EventListener; +import org.dbasu.robomvvm.componentmodel.EventArg; + +import java.lang.ref.WeakReference; + +/** + * Allows binding of properties to properties and events to actions between a source {@link org.dbasu.robomvvm.componentmodel.Component} + * and a target component. Maintains weak references to both the source component and the target component, + * allowing them to be garbage collected even when the binding is alive. When either the source component or the target + * component is garbage collected, the binding is automatically unbound. + */ +public class Binding { + + /** + * Binds an event in the source component to an action in the target component. When an event of type eventType + * is raised by the source component, {@link org.dbasu.robomvvm.componentmodel.Component#invokeAction} is called + * on the target component. + * + * @param source + * The source component. + * @param target + * The target component. + * @param eventType + * The event type in the source component that triggers the action. + * @param action + * The action method name in the target component. The action method may take an argument of the supplied + * eventType, or may take no arguments. + * @return + * The binding created by this call. + */ + public static Binding bindAction(Component source, Component target, Class eventType, String action) { + + Preconditions.checkNotNull(source); + Preconditions.checkNotNull(target); + Preconditions.checkNotNull(eventType); + Preconditions.checkNotNull(action); + + Binding ret = new ActionBinding(source, target, eventType, action); + ret.bind(); + return ret; + + } + + + /** + * Binds a property in the source component to a property in the target component. The directionality of the binding is specified + * using {@link org.dbasu.robomvvm.binding.BindMode}, and conversions between the source value and the target value are carried + * out using a {@link org.dbasu.robomvvm.binding.ValueConverter}. + * + * @param source + * The source component. + * @param sourceProperty + * The source property name. + * @param target + * The target component. + * @param targetProperty + * The target property name. + * @param valueConverter + * The value converter to convert between source and target properties. + * @param bindMode + * The bind mode to be used for this binding. + * @return + * The binding created by this call. + */ + + public static Binding bindProperty(Component source, String sourceProperty, Component target, String targetProperty, ValueConverter valueConverter, BindMode bindMode) { + + Preconditions.checkNotNull(source); + Preconditions.checkNotNull(sourceProperty); + Preconditions.checkNotNull(target); + Preconditions.checkNotNull(targetProperty); + Preconditions.checkNotNull(valueConverter); + Preconditions.checkNotNull(bindMode); + + Binding ret = new PropertyBinding(source, sourceProperty, target, targetProperty, valueConverter, bindMode); + ret.bind(); + return ret; + } + + /** + * Binds a property in the source component to a property in the target component. Changes to the source property + * are reflected in the target property, but not the other way round. This corresponds to + * {@link org.dbasu.robomvvm.binding.BindMode#SOURCE_TO_TARGET}. A {@link org.dbasu.robomvvm.binding.DefaultValueConverter} is used. + * + * @param source + * The source component. + * @param sourceProperty + * The source property name. + * @param target + * The target component. + * @param targetProperty + * The target property name. + * @return + * The binding created by this call. + */ + + public static Binding bindProperty(Component source, String sourceProperty, Component target, String targetProperty) { + + return bindProperty(source, sourceProperty, target, targetProperty, new DefaultValueConverter(), BindMode.SOURCE_TO_TARGET); + + } + + /** + * Binds a property in the source component to a property in the target component. The directionality of the binding is specified + * using {@link org.dbasu.robomvvm.binding.BindMode}. No conversions between the source and the target values is + * performed. + * + * @param source + * The source component. + * @param sourceProperty + * The source property name. + * @param target + * The target component. + * @param targetProperty + * The target property name. + * @param bindMode + * The bind mode to be used for this binding. + * @return + * The binding created by this call. + */ + + public static Binding bindProperty(Component source, String sourceProperty, Component target, String targetProperty, BindMode bindMode) { + + return bindProperty(source, sourceProperty, target, targetProperty, new DefaultValueConverter(), bindMode); + + } + + /** + * Unbind this binding. This method is automatically + * called when the source or the target component is garbage collected. + */ + public void unbind() { + Component source = getSource(); + Component target = getTarget(); + + if(source != null) { + source.removeEventListener(garbageCollectionListener); + } + + if(target != null) { + target.removeEventListener(garbageCollectionListener); + } + + bound = false; + } + + /** + * Returns whether the binding is currently bound. + * @return + * True is the binding is currently bound. False otherwise. + */ + public boolean isBound() { + return bound; + } + + /** + * Gets the source component + * @return + * The source component. + */ + public Component getSource() { + return weakSourceReference.get(); + } + + /** + * Gets the target component + * @return + * The target Component. + */ + public Component getTarget() { + return weakTargetReference.get(); + } + + private final WeakReference weakSourceReference; + private final WeakReference weakTargetReference; + + private final EventListener garbageCollectionListener = new EventListener(GarbageCollectionEventArg.class) { + @Override + public void invoke(EventArg args) { + unbind(); + } + }; + + private boolean bound = false; + + protected Binding(Component source, Component target) { + this.weakSourceReference = new WeakReference(source); + this.weakTargetReference = new WeakReference(target); + + } + + protected void bind() { + + Component source = getSource(); + Component target = getTarget(); + + if(source != null) { + source.addEventListener(garbageCollectionListener); + } + + if(target != null) { + target.addEventListener(garbageCollectionListener); + } + bound = true; + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/binding/DefaultValueConverter.java b/library/src/main/java/org/dbasu/robomvvm/binding/DefaultValueConverter.java new file mode 100644 index 0000000..9f17dd5 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/binding/DefaultValueConverter.java @@ -0,0 +1,47 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.binding; + +/** + * A value converter that performs no conversion. + */ +public class DefaultValueConverter implements ValueConverter { + @Override + public Object convertToTarget(Object value) { + return value; + } + + @Override + public Object convertToSource(Object value) { + return value; + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/binding/PropertyBinding.java b/library/src/main/java/org/dbasu/robomvvm/binding/PropertyBinding.java new file mode 100644 index 0000000..6575ea1 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/binding/PropertyBinding.java @@ -0,0 +1,151 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.binding; + +import org.dbasu.robomvvm.componentmodel.Component; +import org.dbasu.robomvvm.componentmodel.PropertyChangeEventArg; +import org.dbasu.robomvvm.componentmodel.EventArg; +import org.dbasu.robomvvm.componentmodel.EventListener; + + +/** + * Package private class implementing the logic for creating a property binding. + */ +class PropertyBinding extends Binding { + + private final String sourceProperty; + private final String targetProperty; + private final ValueConverter valueConverter; + private final BindMode bindMode; + + + PropertyBinding(Component source, String sourceProperty, Component target, String targetProperty, ValueConverter converter, BindMode bindMode) { + super(source, target); + this.sourceProperty = sourceProperty; + this.targetProperty = targetProperty; + this.valueConverter = converter; + this.bindMode = bindMode; + + } + + private final EventListener targetChangeListener = new EventListener(PropertyChangeEventArg.class) { + @Override + public void invoke(EventArg args) { + String name = ((PropertyChangeEventArg) args).getPropertyName(); + + if(!name.equals(targetProperty)) return; + + Component source = getSource(); + Component target = getTarget(); + + if(source == null || target == null) return; + + + boolean hasListener = source.removeEventListener(sourceChangeListener); + + Object value = target.getProperty(targetProperty); + value = valueConverter.convertToSource(value); + + source.setProperty(sourceProperty, value); + + if(hasListener) { + source.addEventListener(sourceChangeListener); + } + + } + }; + + private final EventListener sourceChangeListener = new EventListener(PropertyChangeEventArg.class) { + @Override + public void invoke(EventArg args) { + String name = ((PropertyChangeEventArg) args).getPropertyName(); + + if(!name.equals(sourceProperty)) return; + + Component source = getSource(); + Component target = getTarget(); + + if(source == null || target == null) return; + + boolean hasListener = target.removeEventListener(targetChangeListener); + + Object value = source.getProperty(sourceProperty); + + value = valueConverter.convertToTarget(value); + + target.setProperty(targetProperty, value); + + if(hasListener) { + target.addEventListener(targetChangeListener); + } + } + }; + + + @Override + public void unbind() { + + Component source = getSource(); + Component target = getTarget(); + + if(source != null) { + source.removeEventListener(sourceChangeListener); + } + + if(target != null) { + target.removeEventListener(targetChangeListener); + } + + super.unbind(); + } + + + @Override + protected void bind() { + + Component source = getSource(); + Component target = getTarget(); + + + if(source!= null && bindMode.canBindSourceToTarget()) { + source.addEventListener(sourceChangeListener); + sourceChangeListener.invoke(new PropertyChangeEventArg(source, sourceProperty)); + } + + if(target != null && bindMode.canBindTargetToSource()) { + target.addEventListener(targetChangeListener); + targetChangeListener.invoke(new PropertyChangeEventArg(target, targetProperty)); + } + + super.bind(); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/binding/TypedValueConverter.java b/library/src/main/java/org/dbasu/robomvvm/binding/TypedValueConverter.java new file mode 100644 index 0000000..75896ae --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/binding/TypedValueConverter.java @@ -0,0 +1,127 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.binding; + +import com.google.common.base.Preconditions; + +import org.apache.commons.lang3.ClassUtils; + +/** + * A value converter that attempts to convert between a source type and a target type. + */ +public class TypedValueConverter implements ValueConverter { + + protected final Class sourceType, targetType; + + /** + * Constructs a typed value converter for converting values between types. + * @param sourceType + * The type to convert the target value to in the function {@link #convertToSource(Object)}. + * @param targetType + * The type to convert the source value to in the function {@link #convertToTarget(Object)}. + */ + public TypedValueConverter(Class sourceType, Class targetType) { + Preconditions.checkNotNull(sourceType); + Preconditions.checkNotNull(targetType); + + this.sourceType = sourceType; + this.targetType = targetType; + } + + + /** + * Attempt to convert the value of the source property to the target type. If the type of the source property + * is assignable to the target type, it performs a cast. If it is not, and the target type is {@link java.lang.String}, + * it calls the the source property's {@link Object#toString()} function. + * + * @param value + * The value of the source property. + * @throws RuntimeException + * When the conversion fails. + * @return + * The result of the conversion of the source property to the target type. + */ + @Override + public Object convertToTarget(Object value) { + + if(value == null) return null; + + Class valueType = value.getClass(); + + + if(ClassUtils.isAssignable(targetType, valueType, true)) { + return targetType.cast(value); + } + + if(ClassUtils.isAssignable(String.class, targetType, true)) { + return targetType.cast(value.toString()); + + } + + throw new RuntimeException("Unsupported Conversion From " + valueType.getName() + " To " + targetType.getName()); + + } + + + /** + * Attempt to convert the value of the target property to the source type. If the type of the target property + * is assignable to the source type, it performs a cast. If it is not, and the source type is {@link java.lang.String}, + * it calls the the target property's {@link Object#toString()} function. + * + * @param value + * The value of the target property. + * @throws RuntimeException + * When the conversion fails. + * @return + * The result of the conversion of the target property to the source type. + */ + @Override + public Object convertToSource(Object value) { + + if(value == null) return null; + + Class valueType = value.getClass(); + + + if(ClassUtils.isAssignable(sourceType, valueType, true)) { + + return sourceType.cast(value); + } + + if(ClassUtils.isAssignable(String.class, sourceType, true)) { + return sourceType.cast(value.toString()); + } + + throw new RuntimeException(this.getClass().getName() + " Does Not Support Conversion From " + valueType.getName() + " To " + sourceType.getName()); + + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/binding/ValueConverter.java b/library/src/main/java/org/dbasu/robomvvm/binding/ValueConverter.java new file mode 100644 index 0000000..5bb1e38 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/binding/ValueConverter.java @@ -0,0 +1,61 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.binding; + +/** + * Converts between source and target values in a {@link org.dbasu.robomvvm.binding.Binding} + */ +public interface ValueConverter { + + /** + * Converts a source value into a target value. This is called whenever a source + * property's value is being applied to the target property in a binding. + * + * @param value + * The value of the source property. + * @return + * The value to apply to the target. + */ + public Object convertToTarget(Object value); + + + /** + * Converts a target value into a source value. This is called whenever a target + * property's value is being applied to the source property in a binding. + * + * @param value + * The value of the source property. + * @return + * The value to apply to the target. + */ + public Object convertToSource(Object value); +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/adapterview/AdapterViewAdapter.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/adapterview/AdapterViewAdapter.java new file mode 100644 index 0000000..6c33f5a --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/adapterview/AdapterViewAdapter.java @@ -0,0 +1,128 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.adapterview; + +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; + +import org.dbasu.robomvvm.componentadapter.view.ViewAdapter; +import org.dbasu.robomvvm.viewmodel.ViewModel; +import org.dbasu.robomvvm.viewmodel.ViewModelCollection; + +import java.lang.reflect.Field; + + +/** + * A view adapter to adapt an AdapterView. Can bind {@link org.dbasu.robomvvm.viewmodel.ViewModelCollection}s to + * AdapterViews. + */ +public class AdapterViewAdapter extends ViewAdapter { + + protected ViewModelCollection source; + + /** + * Set the source view model collection. + * @param source + * The source view model collection. + */ + public void setSource(ViewModelCollection source) { + this.source = source; + AdapterView adapterView = (AdapterView) targetObject; + adapterView.setAdapter(source); + } + + /** + * Set the view model to use when the source view model collection is empty. + * @param value + * The empty view model. + */ + public void setEmptyViewModel(ViewModel value) { + AdapterView adapterView = (AdapterView) targetObject; + View emptyView = value.getView(); + emptyView.setVisibility(View.GONE); + ViewGroup parent = (ViewGroup)adapterView.getParent(); + int index = parent.indexOfChild(adapterView); + parent.addView(emptyView, index + 1); + adapterView.setEmptyView(emptyView); + } + + + private void setSourceSelection(int selection) { + + if(source == null) return; + + Class klass = ViewModelCollection.class; + Field selectedItemField = null; + try { + selectedItemField = klass.getDeclaredField("selectedItem"); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + + selectedItemField.setAccessible(true); + + try { + selectedItemField.set(source, selection); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + + + @Override + protected void adapt() { + super.adapt(); + + final AdapterView adapterView = (AdapterView) targetObject; + + if(adapterView.isFocusable()) { + + adapterView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + setSourceSelection(position); + raiseEvent(new ItemSelectEventArg(AdapterViewAdapter.this, position)); + } + + @Override + public void onNothingSelected(AdapterView parent) { + + setSourceSelection(-1); + raiseEvent(new ItemSelectEventArg(AdapterViewAdapter.this, -1)); + } + }); + } + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/adapterview/ItemSelectEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/adapterview/ItemSelectEventArg.java new file mode 100644 index 0000000..b8895c6 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/adapterview/ItemSelectEventArg.java @@ -0,0 +1,64 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.adapterview; + +import org.dbasu.robomvvm.componentmodel.EventArg; + +/** + * Raised by {@link AdapterViewAdapter} to notify listeners + * when an item in the adapter view is selected. + */ +public class ItemSelectEventArg extends EventArg { + + private final int position; + + /** + * Get the position of the selected item. + * @return + * The position of the selected item. + */ + public int getPosition() { + return position; + } + + /** + * Construct an ItemSelectEventArg. + * @param sender + * The sending AdapterView adapter. + * @param position + * The position of the selected item. + */ + public ItemSelectEventArg(AdapterViewAdapter sender, int position) { + super(sender); + this.position = position; + } +} \ No newline at end of file diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/CheckedChangeEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/CheckedChangeEventArg.java new file mode 100644 index 0000000..39857ec --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/CheckedChangeEventArg.java @@ -0,0 +1,65 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.compoundbutton; + +import org.dbasu.robomvvm.componentmodel.EventArg; + +/** + * Raised by a {@link CompoundButtonViewAdapter} when + * the compound button undergoes a change in its checked status. + */ +public class CheckedChangeEventArg extends EventArg { + + private final boolean checked; + + /** + * Construct a CheckedChangeEventArg. + * @param sender + * The compound button view adapter that sends this event. + * @param checked + * The checked status of the compound button. + */ + public CheckedChangeEventArg(CompoundButtonViewAdapter sender, boolean checked) { + super(sender); + this.checked = checked; + } + + /** + * Get the checked status of the compound button. + * @return + * The checked status of the compound button. + */ + public final boolean isChecked() { + return this.isChecked(); + } + +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/CheckedEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/CheckedEventArg.java new file mode 100644 index 0000000..b984b3e --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/CheckedEventArg.java @@ -0,0 +1,48 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.compoundbutton; + +/** + * Raised by a {@link CompoundButtonViewAdapter} when + * the compound button is checked. + */ +public class CheckedEventArg extends CheckedChangeEventArg { + + /** + * Construct a CheckedEventArg. + * @param sender + * The compound button view adapter that sends this event. + */ + public CheckedEventArg(CompoundButtonViewAdapter sender) { + super(sender, true); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/CompoundButtonViewAdapter.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/CompoundButtonViewAdapter.java new file mode 100644 index 0000000..7ebaa2f --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/CompoundButtonViewAdapter.java @@ -0,0 +1,68 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.compoundbutton; + +import android.widget.CompoundButton; + +import org.dbasu.robomvvm.componentadapter.view.ViewAdapter; + +/** + * View adapter to adapt a CompoundButton. + */ +public class CompoundButtonViewAdapter extends ViewAdapter { + + /** + * Set the checked state of the compound button. + * @param checked + * The checked state of the compound button. + */ + public void setChecked(boolean checked) { + + CompoundButton compoundButton = (CompoundButton) targetObject; + compoundButton.setChecked(checked); + } + + @Override + protected void adapt() { + super.adapt(); + + final CompoundButton compoundButton = (CompoundButton) targetObject; + + compoundButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + raiseEvent(isChecked? new CheckedEventArg(CompoundButtonViewAdapter.this) : new UncheckedEventArg(CompoundButtonViewAdapter.this)); + } + }); + + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/UncheckedEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/UncheckedEventArg.java new file mode 100644 index 0000000..6a1b242 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/compoundbutton/UncheckedEventArg.java @@ -0,0 +1,48 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.compoundbutton; + +/** + * Raised by a {@link CompoundButtonViewAdapter} when + * the compound button is unchecked. + */ +public class UncheckedEventArg extends CheckedChangeEventArg { + + /** + * Construct a UncheckedEventArg. + * @param sender + * The compound button view adapter that sends this event. + */ + public UncheckedEventArg(CompoundButtonViewAdapter sender) { + super(sender, false); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/edittext/EditTextViewAdapter.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/edittext/EditTextViewAdapter.java new file mode 100644 index 0000000..75c1469 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/edittext/EditTextViewAdapter.java @@ -0,0 +1,80 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.edittext; + +import android.text.Editable; +import android.text.TextWatcher; +import android.widget.EditText; + +import org.dbasu.robomvvm.componentadapter.textview.TextViewAdapter; + +/** + * Text view adapter to adapt an EditText. + */ +public class EditTextViewAdapter extends TextViewAdapter { + + /** + * Return the text of the edit text. + * @return + * The text of the edit text. + */ + public String getText() { + EditText editText = (EditText) targetObject; + return editText.getText().toString(); + } + + + @Override + protected void adapt() { + super.adapt(); + + final EditText editText = (EditText) targetObject; + + + editText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + raiseEvent(new TextChangeEventArg(EditTextViewAdapter.this, s, start, before, count)); + } + + @Override + public void afterTextChanged(Editable s) { + + } + }); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/edittext/TextChangeEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/edittext/TextChangeEventArg.java new file mode 100644 index 0000000..4c13f2c --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/edittext/TextChangeEventArg.java @@ -0,0 +1,107 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.edittext; + +import org.dbasu.robomvvm.componentmodel.PropertyChangeEventArg; + +/** + * Raised by the {@link EditTextViewAdapter} when its + * text property changes. + */ +public class TextChangeEventArg extends PropertyChangeEventArg { + + private final CharSequence charSequence; + private final int startLocation; + private final int oldCount; + private final int newCount; + + + /** + * Get the char sequence that changed. + * @return + * The char sequence that changed. + */ + public CharSequence getCharSequence() { + return charSequence; + } + + /** + * Get the start location of the change. + * @return + * The start location of the change. + */ + public int getStartLocation() { + return startLocation; + } + + + /** + * Get the new count of characters beginning from the start location. + * @return + * The new count of characters beginning from the start location. + */ + public int getNewCount() { + return newCount; + } + + + /** + * Get the old count of characters beginning from the start location. + * @return + * The old count of characters beginning from the start location. + */ + public int getOldCount() { + return oldCount; + } + + + /** + * Construct a TextChangeEventArg. + * @param sender + * The edit text view adapter that raises this event. + * @param charSequence + * The char sequence that changed. + * @param startLocation + * The start location of the change. + * @param oldCount + * The old count of characters beginning from the start location. + * @param newCount + * The new count of characters beginning from the start location. + */ + public TextChangeEventArg(EditTextViewAdapter sender, CharSequence charSequence, int startLocation, int oldCount, int newCount) { + super(sender, "text"); + this.charSequence = charSequence; + this.startLocation = startLocation; + this.oldCount = oldCount; + this.newCount = newCount; + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/imageview/ImageViewAdapter.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/imageview/ImageViewAdapter.java new file mode 100644 index 0000000..0e223ca --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/imageview/ImageViewAdapter.java @@ -0,0 +1,74 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.imageview; + +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.widget.ImageView; + +import org.dbasu.robomvvm.componentadapter.view.ViewAdapter; + +/** + * View adapter to adapt an ImageView. + */ +public class ImageViewAdapter extends ViewAdapter { + + /** + * Set the source drawable for the image view. + * @param image + * The source drawable. + */ + public void setSource(Drawable image) { + ImageView imageView = (ImageView) targetObject; + imageView.setImageDrawable(image); + } + + /** + * Set the source bitmap for the image view. + * @param image + * The source bitmap. + */ + public void setSource(Bitmap image) { + ImageView imageView = (ImageView) targetObject; + imageView.setImageBitmap(image); + } + + /** + * Set the source drawable for the image view. + * @param resId + * The resource id of the source drawable. + */ + public void setSource(int resId) { + ImageView imageView = (ImageView) targetObject; + imageView.setImageResource(resId); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/listview/ItemClickEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/listview/ItemClickEventArg.java new file mode 100644 index 0000000..80a9f6b --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/listview/ItemClickEventArg.java @@ -0,0 +1,67 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.listview; + +import org.dbasu.robomvvm.componentmodel.EventArg; + +/** + * Raised by {@link ListViewAdapter} when an item in its list view + * is clicked. + */ +public class ItemClickEventArg extends EventArg { + + private final int position; + + /** + * Gets the position of the item in the list view that is clicked. + * @return + * The position of the item that is clicked. + */ + public int getPosition() { + return position; + } + + + /** + * Construct an ItemClickEventArg. + * @param sender + * The ListView adapter that sends this event. + * @param position + * The position of the item that is clicked. + */ + public ItemClickEventArg(ListViewAdapter sender, int position) { + super(sender); + this.position = position; + + } +} + diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/listview/ItemLongClickEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/listview/ItemLongClickEventArg.java new file mode 100644 index 0000000..15b0c76 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/listview/ItemLongClickEventArg.java @@ -0,0 +1,65 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.listview; + +import org.dbasu.robomvvm.componentmodel.EventArg; + +/** + * Raised by {@link ListViewAdapter} when an item in its list view + * is long clicked. + */ +public class ItemLongClickEventArg extends EventArg { + + private final int position; + + /** + * Gets the position of the item in the list view that is long clicked. + * @return + * The position of the item that is long clicked. + */ + public int getPosition() { + return position; + } + + /** + * Construct an ItemLongClickEventArg. + * @param sender + * The ListView adapter that sends this event. + * @param position + * The position of the item that is long clicked. + */ + public ItemLongClickEventArg(ListViewAdapter sender, int position) { + super(sender); + + this.position = position; + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/listview/ListViewAdapter.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/listview/ListViewAdapter.java new file mode 100644 index 0000000..b5b8200 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/listview/ListViewAdapter.java @@ -0,0 +1,110 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.listview; + +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; + +import org.dbasu.robomvvm.componentadapter.adapterview.AdapterViewAdapter; +import org.dbasu.robomvvm.componentmodel.EventArg; +import org.dbasu.robomvvm.componentmodel.EventListener; +import org.dbasu.robomvvm.viewmodel.ViewModelCollection; +import org.dbasu.robomvvm.viewmodel.ItemCheckedEventArg; + +/** + * AdapterView adapter to adapt a ListView. + */ +public class ListViewAdapter extends AdapterViewAdapter { + + + private ViewModelCollection itemSource; + + + private final EventListener checkedChangeListener = new EventListener(ItemCheckedEventArg.class) { + @Override + public void invoke(EventArg args) { + ItemCheckedEventArg checked = (ItemCheckedEventArg) args; + ListView listView = (ListView) targetObject; + listView.setItemChecked(checked.getPosition(), checked.getValue()); + + } + }; + + /** + * Set the source view model collection. + * @param source + * The source view model collection. + */ + @Override + public void setSource(ViewModelCollection source) { + + super.setSource(source); + if(itemSource != null) { + itemSource.getEventSource().removeEventListener(checkedChangeListener); + } + + itemSource = source; + + if(itemSource != null) { + itemSource.getEventSource().addEventListener(checkedChangeListener); + } + + } + + @Override + protected void adapt() { + super.adapt(); + + ListView listView = (ListView) targetObject; + + + if(listView.isClickable()) { + + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + raiseEvent(new ItemClickEventArg(ListViewAdapter.this, position)); + } + }); + + listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + raiseEvent(new ItemLongClickEventArg(ListViewAdapter.this, position)); + return true; + } + }); + + } + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemAdapter.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemAdapter.java new file mode 100644 index 0000000..a8ce5bc --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemAdapter.java @@ -0,0 +1,160 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.menuitem; + + +import android.graphics.drawable.Drawable; +import android.view.MenuItem; + +import org.dbasu.robomvvm.componentmodel.ComponentAdapter; + +/** + * Component adapter to adapt a MenuItem. + */ +public class MenuItemAdapter extends ComponentAdapter { + + + /** + * Set the checked status of the menu item. + * @param value + * The checked status of the menu item. + */ + public void setChecked(boolean value) { + MenuItem menuItem = (MenuItem) targetObject; + menuItem.setChecked(value); + } + + /** + * Set the icon of the menu item, or remove the icon. + * @param icon + * The Drawable to set as the icon of the menu item, or null to remove the icon. + */ + public void setIcon(Drawable icon) { + MenuItem menuItem = (MenuItem) targetObject; + menuItem.setIcon(icon); + } + + /** + * Set the icon of the menu item, or remove the icon. + * @param iconId + * The resource id of the Drawable to set as the icon of the menu item, + * or 0 to remove the icon. + */ + public void setIcon(int iconId) { + MenuItem menuItem = (MenuItem) targetObject; + menuItem.setIcon(iconId); + } + + /** + * Set the enabled state of the menu item. + * @param enabled + * The enabled state of the menu item. + */ + public void setEnabled(boolean enabled) { + MenuItem menuItem = (MenuItem) targetObject; + menuItem.setEnabled(enabled); + } + + /** + * Set the expanded state of the menu item. + * @param expanded + * The expanded state of the menu item. + */ + public void setExpanded(boolean expanded) { + MenuItem menuItem = (MenuItem) targetObject; + if(expanded) { + menuItem.expandActionView(); + } else { + menuItem.collapseActionView(); + } + } + + /** + * Set the title of the menu item. + * @param title + * The title of the menu item. + */ + public void setTitle(String title) { + MenuItem menuItem = (MenuItem) targetObject; + menuItem.setTitle(title); + } + + /** + * Set the condensed title of the menu item. + * @param condensedTitle + * The condensed title of the menu item. + */ + public void setCondensedTitle(String condensedTitle) { + MenuItem menuItem = (MenuItem) targetObject; + menuItem.setTitleCondensed(condensedTitle); + } + + /** + * Set the visible state of the menu item. + * @param visible + * The visible state of the menu item. + */ + public void setVisible(boolean visible) { + MenuItem menuItem = (MenuItem) targetObject; + menuItem.setVisible(visible); + } + + + @Override + protected void adapt() { + + MenuItem menuItem = (MenuItem) targetObject; + + menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + + raiseEvent(new MenuItemClickEventArg(MenuItemAdapter.this)); + return true; + } + }); + + menuItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { + @Override + public boolean onMenuItemActionExpand(MenuItem menuItem) { + raiseEvent(new MenuItemExpandedChangeEventArg(MenuItemAdapter.this, true)); + return true; + } + + @Override + public boolean onMenuItemActionCollapse(MenuItem menuItem) { + raiseEvent(new MenuItemExpandedChangeEventArg(MenuItemAdapter.this, false)); + return true; + } + }); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemClickEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemClickEventArg.java new file mode 100644 index 0000000..7206b40 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemClickEventArg.java @@ -0,0 +1,50 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.menuitem; + +import org.dbasu.robomvvm.componentmodel.EventArg; + +/** + * Raised by a {@link MenuItemAdapter} when + * a menu item is clicked. + */ +public class MenuItemClickEventArg extends EventArg { + + /** + * Construct a MenuItemClickEventArg. + * @param source + * The menu item adapter that sent this event. + */ + public MenuItemClickEventArg(MenuItemAdapter source) { + super(source); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemCollapsedEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemCollapsedEventArg.java new file mode 100644 index 0000000..5941d06 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemCollapsedEventArg.java @@ -0,0 +1,50 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.menuitem; + + +/** + * Raised by a {@link MenuItemAdapter} when its + * menu item is collapsed. + */ +public class MenuItemCollapsedEventArg extends MenuItemExpandedChangeEventArg { + + /** + * Construct a MenuItemCollapsedEventArg. + * + * @param sender + * The menu item adapter that sent this event. + */ + public MenuItemCollapsedEventArg(MenuItemAdapter sender) { + super(sender, false); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemExpandedChangeEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemExpandedChangeEventArg.java new file mode 100644 index 0000000..f67e576 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemExpandedChangeEventArg.java @@ -0,0 +1,64 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.menuitem; + +import org.dbasu.robomvvm.componentmodel.PropertyChangeEventArg; + +/** + * Raised by a {@link MenuItemAdapter} when + * the expanded state of a menu item is changed. + */ +public class MenuItemExpandedChangeEventArg extends PropertyChangeEventArg { + + private final boolean expanded; + + /** + * Construct a MenuItemExpandedChangeEventArg. + * @param sender + * The menu item adapter that sent this event. + * @param expanded + * The expanded state of the menu item. + */ + public MenuItemExpandedChangeEventArg(MenuItemAdapter sender, boolean expanded) { + super(sender, "expanded"); + this.expanded = expanded; + } + + /** + * Gets the expanded state of the menu item. + * @return + * The expanded state of the menu item. + */ + public boolean isExpanded() { + return expanded; + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemExpandedEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemExpandedEventArg.java new file mode 100644 index 0000000..154b98c --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/menuitem/MenuItemExpandedEventArg.java @@ -0,0 +1,50 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.menuitem; + + +/** + * Raised by a {@link MenuItemAdapter} when its + * menu item is expanded. + */ +public class MenuItemExpandedEventArg extends MenuItemExpandedChangeEventArg { + + /** + * Construct a MenuItemExpandedEventArg. + * + * @param sender + * The menu item adapter that sent this event. + */ + public MenuItemExpandedEventArg(MenuItemAdapter sender) { + super(sender, true); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/progressbar/ProgressBarViewAdapter.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/progressbar/ProgressBarViewAdapter.java new file mode 100644 index 0000000..a3f9e38 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/progressbar/ProgressBarViewAdapter.java @@ -0,0 +1,74 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.progressbar; + +import android.widget.ProgressBar; + +import org.dbasu.robomvvm.componentadapter.view.ViewAdapter; + +/** + * View adapter to adapt a ProgressBar + */ +public class ProgressBarViewAdapter extends ViewAdapter { + + /** + * Set the max extent of the progress bar. + * @param value + * The max extent of the progress bar. + */ + public void setMax(int value) { + + ProgressBar progressBar = (ProgressBar) targetObject; + progressBar.setMax(value); + } + + /** + * Set the progress value of the progress bar. + * @param value + * The progress value of the progress bar. + */ + public void setProgress(int value) { + ProgressBar progressBar = (ProgressBar) targetObject; + progressBar.setProgress(value); + } + + /** + * Set the secondary progress of the progress bar. + * @param value + * The secondary progress of the progress bar. + */ + public void setSecondaryProgress(int value) { + ProgressBar progressBar = (ProgressBar) targetObject; + progressBar.setSecondaryProgress(value); + } + +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/ratingbar/RatingBarViewAdapter.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/ratingbar/RatingBarViewAdapter.java new file mode 100644 index 0000000..6070b21 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/ratingbar/RatingBarViewAdapter.java @@ -0,0 +1,79 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.ratingbar; + + +import android.widget.RatingBar; + +import org.dbasu.robomvvm.componentadapter.progressbar.ProgressBarViewAdapter; + +/** + * ProgressBar view adaptter to adapt a RatingBar. + */ +public class RatingBarViewAdapter extends ProgressBarViewAdapter { + + /** + * Get the rating value of the rating bar. + * @return + * The rating value of the rating bar. + */ + public float getRating() { + RatingBar ratingBar = (RatingBar) targetObject; + return ratingBar.getRating(); + } + + /** + * Set the rating value of the rating bar. + * @param rating + * The rating value of the rating bar. + */ + public void setRating(float rating) { + RatingBar ratingBar = (RatingBar) targetObject; + ratingBar.setRating(rating); + } + + + @Override + protected void adapt() { + super.adapt(); + + final RatingBar ratingBar = (RatingBar) targetObject; + + ratingBar.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener() { + @Override + public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) { + raiseEvent(new RatingChangeEventArg(RatingBarViewAdapter.this, rating, fromUser)); + } + }); + } + +} \ No newline at end of file diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/ratingbar/RatingChangeEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/ratingbar/RatingChangeEventArg.java new file mode 100644 index 0000000..68d05f9 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/ratingbar/RatingChangeEventArg.java @@ -0,0 +1,78 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +package org.dbasu.robomvvm.componentadapter.ratingbar; + +import org.dbasu.robomvvm.componentmodel.PropertyChangeEventArg; + +/** + * Raised by a {@link RatingBarViewAdapter} to notify when the + * rating bar's rating has changed. + */ +public class RatingChangeEventArg extends PropertyChangeEventArg { + + private final float rating; + private final boolean fromUser; + + /** + * Gets the new rating of the rating bar. + * @return + * The new rating of the rating bar. + */ + public float getRating() { + return rating; + } + + /** + * Gets whether the user changed the rating. + * @return + * True if the user changed the rating. False otherwise. + */ + public boolean isFromUser() { + return fromUser; + } + + /** + * Construct a RatingChangeEventArg. + * @param sender + * The RatingBarViewAdapter that sent this event. + * @param rating + * The new rating of the rating bar. + * @param fromUser + * Whether the user changed the rating. + */ + public RatingChangeEventArg(RatingBarViewAdapter sender, float rating, boolean fromUser) { + super(sender, "rating"); + this.rating = rating; + this.fromUser = fromUser; + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/seekbar/ProgressChangeEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/seekbar/ProgressChangeEventArg.java new file mode 100644 index 0000000..d5d03d7 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/seekbar/ProgressChangeEventArg.java @@ -0,0 +1,80 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +package org.dbasu.robomvvm.componentadapter.seekbar; + +import org.dbasu.robomvvm.componentmodel.PropertyChangeEventArg; + +/** + * Raised by a {@link SeekBarViewAdapter} to notify listeners + * when the seek bar's progress changes. + */ +public class ProgressChangeEventArg extends PropertyChangeEventArg { + + private final int progress; + private final boolean fromUser; + + /** + * Construct a ProgressChangeEventArg. + * @param sender + * The SeekBarViewAdapter that sends this event. + * @param progress + * The new progress of the seek bar. + * @param fromUser + * Whether the progress is changed by the user. + */ + public ProgressChangeEventArg(SeekBarViewAdapter sender, int progress, boolean fromUser) { + super(sender, "progress"); + this.progress = progress; + this.fromUser = fromUser; + } + + + /** + * Gets the new progress of the seek bar. + * @return + * The new progress of the seek bar. + */ + public int getProgress() { + return progress; + } + + + /** + * Gets whether the progress is changed by the user. + * @return + * True if the progress is changed by the user. False otherwise. + */ + public boolean isFromUser() { + return fromUser; + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/seekbar/SeekBarViewAdapter.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/seekbar/SeekBarViewAdapter.java new file mode 100644 index 0000000..c52c38e --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/seekbar/SeekBarViewAdapter.java @@ -0,0 +1,77 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.seekbar; + +import android.widget.SeekBar; + +import org.dbasu.robomvvm.componentadapter.progressbar.ProgressBarViewAdapter; + +/** + * ProgressBar view adapter to adapt a SeekBar. + */ +public class SeekBarViewAdapter extends ProgressBarViewAdapter { + + /** + * Gets the progress of the seek bar. + * @return + * The progress of the seek bar. + */ + public int getProgress() { + SeekBar seekBar = (SeekBar) targetObject; + return seekBar.getProgress(); + } + + @Override + protected void adapt() { + super.adapt(); + + final SeekBar seekBar = (SeekBar) targetObject; + + seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + raiseEvent(new ProgressChangeEventArg(SeekBarViewAdapter.this, progress, fromUser)); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + + } + }); + + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/textview/TextViewAdapter.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/textview/TextViewAdapter.java new file mode 100644 index 0000000..840a083 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/textview/TextViewAdapter.java @@ -0,0 +1,62 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.textview; + +import android.widget.TextView; + +import org.dbasu.robomvvm.componentadapter.view.ViewAdapter; + +/** + * View adapter to adapt a TextView. + */ +public class TextViewAdapter extends ViewAdapter { + + /** + * Sets the text of the text view. + * @param text + * The text of the text view. + */ + public void setText(String text) { + TextView textView = (TextView) targetObject; + textView.setText(text); + } + + /** + * Sets the text color of the text view. + * @param color + * The text color of the text view. + */ + public void setTextColor(int color) { + TextView textView = (TextView) targetObject; + textView.setTextColor(color); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/ClickEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/ClickEventArg.java new file mode 100644 index 0000000..52bf04f --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/ClickEventArg.java @@ -0,0 +1,50 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.view; + +import org.dbasu.robomvvm.componentmodel.EventArg; + +/** + * Raised by a {@link ViewAdapter} to notify listeners + * when a view is clicked. + */ +public class ClickEventArg extends EventArg { + + /** + * Construct a ClickEventArg + * @param sender + * The view adapter that sends this event. + */ + public ClickEventArg(ViewAdapter sender) { + super(sender); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/FocusChangeEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/FocusChangeEventArg.java new file mode 100644 index 0000000..86765cf --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/FocusChangeEventArg.java @@ -0,0 +1,65 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.view; + +import org.dbasu.robomvvm.componentmodel.EventArg; + +/** + * Raised by a {@link ViewAdapter} to notify listeners + * when a view undergoes a focus change. + */ +public class FocusChangeEventArg extends EventArg { + + private final boolean hasFocus; + + /** + * Construct a FocusChangeEventArg. + * @param sender + * The view adapter that sends this event. + * @param hasFocus + * The focus state of the view. + */ + public FocusChangeEventArg(ViewAdapter sender, boolean hasFocus) { + super(sender); + this.hasFocus = hasFocus; + } + + /** + * Get the focus state of the view. + * @return + * The focus state of the view. + */ + public final boolean hasFocus() { + return this.hasFocus; + } + +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/FocusEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/FocusEventArg.java new file mode 100644 index 0000000..dd95ad1 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/FocusEventArg.java @@ -0,0 +1,48 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.view; + +/** + * Raised by a {@link ViewAdapter} to notify listeners + * when a view has gained focus. + */ +public class FocusEventArg extends FocusChangeEventArg { + + /** + * Construct a FocusEventArg + * @param sender + * The view adapter that sends this event. + */ + public FocusEventArg(ViewAdapter sender) { + super(sender, true); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/FocusLostEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/FocusLostEventArg.java new file mode 100644 index 0000000..1a4c2bb --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/FocusLostEventArg.java @@ -0,0 +1,49 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.view; + + +/** + * Raised by a {@link ViewAdapter} to notify listeners + * when a view has lost focus. + */ +public class FocusLostEventArg extends FocusChangeEventArg { + + /** + * Construct a FocusLostEventArg + * @param sender + * The view adapter that sends this event. + */ + public FocusLostEventArg(ViewAdapter sender) { + super(sender, false); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/LongClickEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/LongClickEventArg.java new file mode 100644 index 0000000..e609cda --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/LongClickEventArg.java @@ -0,0 +1,51 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.view; + +import org.dbasu.robomvvm.componentmodel.EventArg; + + +/** + * Raised by a {@link ViewAdapter} to notify listeners + * when a view is long clicked. + */ +public class LongClickEventArg extends EventArg { + + /** + * Construct a LongClickEventArg + * @param sender + * The view adapter that sends this event. + */ + public LongClickEventArg(ViewAdapter sender) { + super(sender); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/ViewAdapter.java b/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/ViewAdapter.java new file mode 100644 index 0000000..693de77 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentadapter/view/ViewAdapter.java @@ -0,0 +1,139 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentadapter.view; + +import android.graphics.drawable.Drawable; +import android.view.View; + +import android.widget.AdapterView; + +import org.dbasu.robomvvm.componentmodel.ComponentAdapter; + + +/** + * A component adapter to adapt views. + */ +public class ViewAdapter extends ComponentAdapter { + + + /** + * Set the background to a given resource, or remove the background. + * @param resId + * The identifier of the resource to use, or 0 to remove the background. + */ + public void setBackground(int resId) { + View view = (View) targetObject; + view.setBackgroundResource(resId); + } + + + /** + * Set the background to a given Drawable, or remove the background. + * @param background + * The Drawable to use as the background, or null to remove the background. + */ + public void setbackground(Drawable background) { + View view = (View) targetObject; + view.setBackground(background); + } + + /** + * Sets the background color for this view. + * @param color + * The color of the background. + */ + public void setBackgroundColor(int color) { + View view = (View) targetObject; + view.setBackgroundColor(color); + } + + /** + * Set the enabled state of this view. + * @param enabled + * True if this view is enabled. False otherwise. + */ + public void setEnabled(boolean enabled) { + View view = (View) targetObject; + view.setEnabled(enabled); + } + + /** + * Set the visibility state of this view. + * @param visibility + * One of View.VISIBLE, View.INVISIBLE, or View.GONE. + */ + public void setVisible(int visibility) { + View view = (View) targetObject; + view.setVisibility(visibility); + } + + @Override + protected void adapt() { + + View view = (View) targetObject; + if(view.isFocusable()) { + + view.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + raiseEvent(hasFocus ? new FocusEventArg(ViewAdapter.this) : new FocusLostEventArg(ViewAdapter.this)); + } + }); + } + + if(view instanceof AdapterView) return; + + if(view.isClickable()) { + + + view.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + + raiseEvent(new LongClickEventArg(ViewAdapter.this)); + return true; + } + }); + + + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + raiseEvent(new ClickEventArg(ViewAdapter.this)); + } + }); + + } + + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentmodel/ActionManager.java b/library/src/main/java/org/dbasu/robomvvm/componentmodel/ActionManager.java new file mode 100644 index 0000000..f4ed240 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentmodel/ActionManager.java @@ -0,0 +1,176 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentmodel; + +import android.app.Activity; +import android.view.View; + +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; + +import org.apache.commons.lang3.ClassUtils; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Package private class used for invoking actions by {@link org.dbasu.robomvvm.componentmodel.Component}. + */ +class ActionManager { + + private static class ActionDescriptor { + + private final Collection methods; + private final String name; + private final Class objectType; + + + private ActionDescriptor(Class objectType, String name, Collection methods) { + this.methods = methods; + this.name = name; + this.objectType = objectType; + } + } + + private static final ThreadLocal instance = new ThreadLocal() { + + @Override protected synchronized ActionManager initialValue() { + return new ActionManager(); + } + }; + + static ActionManager get() { + + return instance.get(); + } + + private ActionManager() { + + } + + private final Map actionMap = new HashMap(); + + + private void reallyInvokeAction(Collection handler, Object targetObject, Object eventArg) { + try { + for(Method m : handler) { + if(m.getParameterTypes().length == 0) { + m.invoke(targetObject); + } else { + m.invoke(targetObject, eventArg); + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + + void invokeAction(final Object targetObject, String actionName, final EventArg eventArg) { + + Class targetType = targetObject.getClass(); + final Class eventType = eventArg!= null? eventArg.getClass() : null; + + final ActionDescriptor handlerDescription = getActionDescriptor(targetType, actionName); + + if(handlerDescription == null) { + throw new RuntimeException("No Action By Name " + actionName + + " Found In Class " + targetType.getName()); + } + + final Collection relevantMethods = Collections2.filter(handlerDescription.methods, new Predicate() { + @Override + public boolean apply(Method m) { + + Class[] params = m.getParameterTypes(); + + return params.length == 0 || (eventType != null && params.length == 1 && ClassUtils.isAssignable(eventType, params[0], true)); + + } + }); + + if(relevantMethods.size() == 0) { + + throw new RuntimeException("Action By Name " + actionName + " Does Not Support Actions Of Type " + + ((eventType == null)? " {null} " : eventType.getName())); + } + + if(targetObject instanceof View) { + Activity activity = (Activity) ((View) targetObject).getContext(); + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + reallyInvokeAction(relevantMethods, targetObject, eventArg); + } + }); + } + else { + reallyInvokeAction(relevantMethods, targetObject, eventArg); + } + } + + + private ActionDescriptor reallyGetActionDescriptor(Class objectType, final String actionName) { + + Collection methodList = Collections2.filter(Arrays.asList(objectType.getMethods()), new Predicate() { + @Override + public boolean apply(Method m) { + return m.getName().equals(actionName) && m.getReturnType().equals(Void.TYPE) && m.getParameterTypes().length <= 1; + } + }); + + return new ActionDescriptor(objectType, actionName, methodList); + } + + private ActionDescriptor getActionDescriptor(Class objectType, String actionName) { + + String hash = objectType.getName() + "." + actionName; + + ActionDescriptor ret = actionMap.get(hash); + + if(ret != null) return ret; + + ret = reallyGetActionDescriptor(objectType, actionName); + + if(ret == null) return null; + + actionMap.put(hash, ret); + + return ret; + + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentmodel/Component.java b/library/src/main/java/org/dbasu/robomvvm/componentmodel/Component.java new file mode 100644 index 0000000..741cd67 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentmodel/Component.java @@ -0,0 +1,229 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.dbasu.robomvvm.componentmodel; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; + +import org.apache.commons.lang3.ClassUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Container for events, properties, and actions. Add an {@link org.dbasu.robomvvm.componentmodel.EventListener} to listen + * for events of a specific type. Use {@link org.dbasu.robomvvm.binding.Binding} to bind a property in a + * source component to a property in a target component, or an event in a source component to an action in a + * target component. + */ +public class Component { + + private final List listenerList = new ArrayList(); + + /** + * Adds an event listener to this component. + * + * @param listener + * The listener to add. + */ + public synchronized void addEventListener(EventListener listener) { + + Preconditions.checkNotNull(listener); + + if(!listenerList.contains(listener)) { + listenerList.add(listener); + } + } + + /** + * Checks whether an event listener is already added to this component. + * + * @param listener + * The event listener to check for. + * @return + * True if the listener has been already added. False otherwise. + */ + public synchronized boolean hasEventListener(EventListener listener) { + Preconditions.checkNotNull(listener); + + return listenerList.contains(listener); + } + + /** + * Removes an event listener from this component. + * + * @param listener + * The event listener to remove. + * @return + * True if the listener existed and was removed. False otherwise. + */ + public synchronized boolean removeEventListener(EventListener listener) { + + Preconditions.checkNotNull(listener); + + return listenerList.remove(listener); + } + + /** + * Gets all attached event listeners of a particular type. + * + * @param type + * The to event arg class to query for. + * @param + * Generic parameter representing the event arg class. + * @return + * List of all event listeners corresponding to the given type, or an empty list + * if no corresponding listeners are found. + */ + public synchronized List getEventListenersOfType(final Class type) { + + Preconditions.checkNotNull(type); + + Collection ret = Collections2.filter(listenerList, new Predicate() { + @Override + public boolean apply(EventListener input) { + + return ClassUtils.isAssignable(type, input.getEventType(), true); + } + }); + + return new ArrayList(ret); + } + + /** + * Raise an event with an event arg. All event listeners corresponding to the type of the supplied event arg + * are notified. + * + * @param args + * The supplied event arg. + */ + public void raiseEvent(EventArg args) { + Preconditions.checkNotNull(args); + + List listenerList = this.getEventListenersOfType(args.getClass()); + + for(EventListener l : listenerList) { + l.invoke(args); + } + } + + /** + * Raises an event of type {@link org.dbasu.robomvvm.componentmodel.PropertyChangeEventArg}. + * + * @param propertyName + * The name of the changed property. + */ + public void raisePropertyChangeEvent(String propertyName) { + Preconditions.checkNotNull(propertyName); + + raiseEvent(new PropertyChangeEventArg(this, propertyName)); + } + + + /** + * Sets a property with a given name to a value. A setter function is chosen based on the supplied property name + * and the type of the supplied value. For example, if the name of the property is "foo" and the supplied value is + * of type int, this will attempt to call {@code setFoo(int)}. + * + * @throws java.lang.RuntimeException + * When a corresponding setter function is not found in this component. + * @param name + * The name of the property. + * @param value + * The value to set the property to. + */ + public void setProperty(String name, Object value) { + Preconditions.checkNotNull(name); + + PropertyManager.get().setProperty(this, name, value); + } + + + /** + * Gets the value of a property with a given name. A getter function is called corresponding to the supplied + * property name. For example, if the supplied property name is "foo", this will attempt to call {@code getFoo()}. + * + * @throws java.lang.RuntimeException + * When a corresponding getter function is not found in this component. + * @param name + * The name of the property. + * @return + * The value of the property obtained by the getter function. + */ + public Object getProperty(String name) { + Preconditions.checkNotNull(name); + return PropertyManager.get().getProperty(this, name); + } + + + /** + * Invoke an action with a supplied name and event arg. Calls all functions in this component whose names match the + * supplied name, have a void return type, and have either a single argument of a type compatible with the supplied event arg, + * or no arguments. + * + * @throws java.lang.RuntimeException + * When a corresponding action function is not found in this component. + * @param name + * The name of the action function. + * @param eventArg + * The supplied argument. All action functions with the given name and an argument type matching this eventArg's type + * are called. + */ + public void invokeAction(String name, EventArg eventArg) { + Preconditions.checkNotNull(name); + ActionManager.get().invokeAction(this, name, eventArg); + } + + + /** + * Invoke an action with a supplied name and no arguments. Calls a function in this component whose name matches the + * supplied name, has a void return type, and has no arguments. + * + * @throws java.lang.RuntimeException + * When a corresponding action function is not found in this component. + * @param name + * The name of the action function. + */ + public void invokeAction(String name) { + invokeAction(name, null); + } + + @Override + protected void finalize() throws Throwable { + raiseEvent(new GarbageCollectionEventArg(this)); + super.finalize(); + } + +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentmodel/ComponentAdapter.java b/library/src/main/java/org/dbasu/robomvvm/componentmodel/ComponentAdapter.java new file mode 100644 index 0000000..a92a2fe --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentmodel/ComponentAdapter.java @@ -0,0 +1,229 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentmodel; + +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.RatingBar; +import android.widget.SeekBar; +import android.widget.TextView; + +import com.google.common.base.Preconditions; + +import org.dbasu.robomvvm.componentadapter.adapterview.AdapterViewAdapter; +import org.dbasu.robomvvm.componentadapter.compoundbutton.CompoundButtonViewAdapter; +import org.dbasu.robomvvm.componentadapter.edittext.EditTextViewAdapter; +import org.dbasu.robomvvm.componentadapter.imageview.ImageViewAdapter; +import org.dbasu.robomvvm.componentadapter.listview.ListViewAdapter; +import org.dbasu.robomvvm.componentadapter.menuitem.MenuItemAdapter; +import org.dbasu.robomvvm.componentadapter.progressbar.ProgressBarViewAdapter; +import org.dbasu.robomvvm.componentadapter.ratingbar.RatingBarViewAdapter; +import org.dbasu.robomvvm.componentadapter.seekbar.SeekBarViewAdapter; +import org.dbasu.robomvvm.componentadapter.textview.TextViewAdapter; +import org.dbasu.robomvvm.componentadapter.view.ViewAdapter; +import org.dbasu.robomvvm.util.ObjectTagger; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * An adapter to make components from arbitrary objects. Make associations between object classes and component adapters + * classes using {@link ComponentAdapter.Associations}. Create or retrieve the component adapter + * of an object of one of the associated types or their subtypes using {@link #get(Object)}. + */ +public class ComponentAdapter extends Component { + + private static final String COMPONENT_ADAPTER = "robomvvm_component_adapter"; + + /** + * Can make associations between arbitrary classes and the {@link ComponentAdapter} subclasses that are used to make + * components out of them. + */ + public static class Associations { + + static { + adapterTypes = new HashMap, Class>(); + + set(MenuItem.class, MenuItemAdapter.class); + set(View.class, ViewAdapter.class); + set(ImageView.class, ImageViewAdapter.class); + set(TextView.class, TextViewAdapter.class); + set(EditText.class, EditTextViewAdapter.class); + set(CompoundButton.class, CompoundButtonViewAdapter.class); + set(ProgressBar.class, ProgressBarViewAdapter.class); + set(SeekBar.class, SeekBarViewAdapter.class); + set(RatingBar.class, RatingBarViewAdapter.class); + set(AdapterView.class, AdapterViewAdapter.class); + set(ListView.class, ListViewAdapter.class); + } + + private static final Map, Class> adapterTypes; + + /** + * Get the associated component adapter class associated with a class. If no + * component adapter class is associated with the supplied class, then its super classes and interfaces + * are searched until a component adapter association is found. + * @param objectType + * The class of the object to find associations for. + * @return + * The associated component adapter class, or null if none is found. + */ + public synchronized static Class get(Class objectType) { + Preconditions.checkNotNull(objectType); + + Class adapterType = null; + + Class myObjectType = objectType; + + List> reachableObjectTypes = new ArrayList>(); + + while (myObjectType!= null) { + + reachableObjectTypes.add(myObjectType); + reachableObjectTypes.addAll(Arrays.asList(myObjectType.getInterfaces())); + + myObjectType = myObjectType.getSuperclass(); + } + + for(Class klazz : reachableObjectTypes) { + adapterType = adapterTypes.get(klazz); + if(adapterType != null) return adapterType; + + } + + return null; + } + + + /** + * Associates a component adapter class with an arbitrary class. + * @param objectType + * The class to make the association for. If an association already exists for this class, then it is overwritten. + * @param adapterType + * The component adapter class to associate with the supplied class. + * If this is null, then any existing association of the supplied class is removed. + */ + public synchronized static void set(Class objectType, Class adapterType) { + Preconditions.checkNotNull(objectType); + + if(adapterType != null) { + adapterTypes.put(objectType, adapterType); + } + else { + adapterTypes.remove(objectType); + } + } + } + + + /** + * Create or retrieve the component adapter for the supplied object. Once created, the component adapter is stored as a tag + * on the supplied object using {@link org.dbasu.robomvvm.util.ObjectTagger}, which stores weak references to the supplied object to + * allow for Garbage Collection. Any component adapter associated with an object that has been garbage collected is not stored + * internally by the library. + * + * @param targetObject + * The object to get the component adapter for. + * @return + * The component adapter for the supplied object. + * + * @throws java.lang.RuntimeException + * When no component adapter class is associated with + * the class of the supplied object. + */ + public synchronized static ComponentAdapter get(Object targetObject) { + Preconditions.checkNotNull(targetObject); + + ComponentAdapter componentAdapter = (ComponentAdapter) ObjectTagger.getTag(targetObject, COMPONENT_ADAPTER); + if (componentAdapter != null) return componentAdapter; + + Class adapterType = Associations.get(targetObject.getClass()); + + if(adapterType == null) { + + throw new RuntimeException("No ComponentAdapter Subclass Associated With " + targetObject.getClass().getName() + + ". Call ComponentAdapter.Associations.set(Class objectType, Class adapterType) " + + "To Make The Association."); + } + + try { + componentAdapter = adapterType.newInstance(); + + } catch (Exception e) { + throw new RuntimeException(e); + } + + componentAdapter.init(targetObject); + + ObjectTagger.setTag(targetObject, COMPONENT_ADAPTER, componentAdapter); + + return componentAdapter; + } + + protected Object targetObject; + + protected ComponentAdapter() { + + } + + /** + * Gets the target object of this component adapter. + * @return + * Returns the target object. + */ + public Object getTargetObject() { + return targetObject; + } + + private void init(Object targetObject) { + this.targetObject = targetObject; + adapt(); + + } + + /** + * Call this function in subclasses to set up the component adapter. + */ + protected void adapt() { + + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentmodel/EventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentmodel/EventArg.java new file mode 100644 index 0000000..c61003c --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentmodel/EventArg.java @@ -0,0 +1,61 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentmodel; + +import com.google.common.base.Preconditions; + +/** + * Argument type of an event. Each event is uniquely identified by its argument type. + */ +public class EventArg { + + private final Component source; + + /** + * Construct an Event Arg for a source component. + * @param source + * The source component that raises this event. + */ + public EventArg(Component source) { + this.source = Preconditions.checkNotNull(source);; + } + + /** + * Get the source component that raised this event. + * @return + * The source component. + */ + Component getSource() { + return source; + } + +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentmodel/EventListener.java b/library/src/main/java/org/dbasu/robomvvm/componentmodel/EventListener.java new file mode 100644 index 0000000..439fd95 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentmodel/EventListener.java @@ -0,0 +1,73 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentmodel; + +import com.google.common.base.Preconditions; + +/** + * An event listener that listens to events of a particular {@link org.dbasu.robomvvm.componentmodel.EventArg} class. + */ +public abstract class EventListener { + + final Class eventType; + + /** + * Construct an event listener of a particular event arg class. + * + * @param eventType + * The event arg class to listen for. + * @param + * Generic parameter representing the event arg class to listen for. + */ + public EventListener(Class eventType) { + + this.eventType = Preconditions.checkNotNull(eventType); + } + + /** + * Gets the event arg class that this event listener listens for. + * + * @return + * The event arg class. + */ + public Class getEventType() { + return eventType; + } + + /** + * Abstract function that is called when an event of the associated event arg class is invoked. + * Override in subclasses to implement custom listener behavior. + * @param args + * The event arg instance. + */ + public abstract void invoke(EventArg args); +} diff --git a/library/src/main/java/org/dbasu/robomvvm/componentmodel/GarbageCollectionEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentmodel/GarbageCollectionEventArg.java new file mode 100644 index 0000000..a6f432d --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentmodel/GarbageCollectionEventArg.java @@ -0,0 +1,47 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentmodel; + +/** + * Used to notify listeners when a {@link org.dbasu.robomvvm.componentmodel.Component} is garbage collected. + */ +public class GarbageCollectionEventArg extends EventArg { + + /** + * Constructs a GarbageCollectionEventArg that notifies of a garbage collection. + * @param source + * The component that is garbage collected. + */ + public GarbageCollectionEventArg(Component source) { + super(source); + } +} \ No newline at end of file diff --git a/library/src/main/java/org/dbasu/robomvvm/componentmodel/PropertyChangeEventArg.java b/library/src/main/java/org/dbasu/robomvvm/componentmodel/PropertyChangeEventArg.java new file mode 100644 index 0000000..99414ae --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentmodel/PropertyChangeEventArg.java @@ -0,0 +1,61 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentmodel; + +/** + * Used to notify listeners when a property in a {@link org.dbasu.robomvvm.componentmodel.Component} changes. + */ +public class PropertyChangeEventArg extends EventArg { + + private final String propertyName; + + /** + * Constructs a PropertyChangeEventArg that notifies of a property change. + * @param sender + * The component whose property has changed. + * @param propertyName + * The name of the property that changed. + */ + public PropertyChangeEventArg(Component sender, String propertyName) { + super(sender); + this.propertyName = propertyName; + } + + /** + * Get the name of the changed property. + * @return + * The name of the changed property. + */ + public String getPropertyName() { + return propertyName; + } +} \ No newline at end of file diff --git a/library/src/main/java/org/dbasu/robomvvm/componentmodel/PropertyManager.java b/library/src/main/java/org/dbasu/robomvvm/componentmodel/PropertyManager.java new file mode 100644 index 0000000..b56ba18 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/componentmodel/PropertyManager.java @@ -0,0 +1,205 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.componentmodel; + +import android.app.Activity; +import android.view.View; + +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.Iterables; + +import org.apache.commons.lang3.ClassUtils; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Package private class used for setting and getting properties by {@link org.dbasu.robomvvm.componentmodel.Component}. + */ +class PropertyManager { + + private static class PropertyDescriptor { + private final Method getter; + private final Collection setters; + private final String name; + private final Class objectType; + + PropertyDescriptor(Class objectType, String name, Collection setters, Method getter) { + this.name = name; + this.objectType = objectType; + this.setters= setters; + this.getter = getter; + } + } + + private static final ThreadLocal instance = new ThreadLocal() { + + @Override protected synchronized PropertyManager initialValue() { + return new PropertyManager(); + } + }; + + static PropertyManager get() { + + return instance.get(); + } + + private PropertyManager() { } + + private final Map propertyMap = new HashMap(); + + private PropertyDescriptor getPropertyDescriptor(Class objectType, String name) { + + String hash = objectType.getName() + "." + name; + + PropertyDescriptor ret = propertyMap.get(hash); + + if(ret != null) return ret; + + ret = reallyGetPropertyDescriptor(objectType, name); + + if(ret == null) return null; + + propertyMap.put(hash, ret); + + return ret; + } + + private PropertyDescriptor reallyGetPropertyDescriptor(Class objectType, String name) { + + + final String setName = "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1); + + List allMethods = Arrays.asList(objectType.getMethods()); + + Collection setMethodCollection = Collections2.filter(allMethods, new Predicate() { + + @Override + public boolean apply(Method m) { + Class[] params = m.getParameterTypes(); + return (m.getReturnType().equals(Void.TYPE) && m.getName().equals(setName) && params.length == 1); + } + }); + + String getName = "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1); + Method getMethod = null; + + try { + getMethod = objectType.getMethod(getName); + } catch (NoSuchMethodException e) { + getName = "is" + Character.toUpperCase(name.charAt(0)) + name.substring(1); + try { + getMethod = objectType.getMethod(getName); + } catch (NoSuchMethodException e1) { + + } + } + + if(getMethod == null && setMethodCollection.size() == 0) return null; + return new PropertyDescriptor(objectType, name, setMethodCollection, getMethod); + } + + private void reallySetProperty(Method setter, Object targetObject, Object value) { + + try { + setter.invoke(targetObject, value); + } catch (Exception e) { + e.printStackTrace(); + } + } + + void setProperty(final Object targetObject, String name, final Object value) { + + Class targetType = targetObject.getClass(); + + final PropertyDescriptor desc = getPropertyDescriptor(targetType, name); + if(desc == null || desc.setters.size() == 0) { + throw new RuntimeException("No Settable Property By Name " + name + " Found In Class " + targetType.getName()); + } + + final Class valueClass = value.getClass(); + + + final Method setter = Iterables.find(desc.setters, new Predicate() { + @Override + public boolean apply(Method m) { + return ClassUtils.isAssignable(valueClass, m.getParameterTypes()[0], true); + } + }, null); + + if(setter == null) { + throw new RuntimeException("Type Mismatch: Can Not Assign Value Of Type " + valueClass.getName() + + " To Property " + name + " In Class " + targetType.getName()); + } + + if(targetObject instanceof View) { + + Activity activity = (Activity) ((View) targetObject).getContext(); + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + reallySetProperty(setter, targetObject, value); + } + }); + } + else { + reallySetProperty(setter, targetObject, value); + } + } + + Object getProperty(Object targetObject, String name) { + + Class targetType = targetObject.getClass(); + + PropertyManager.PropertyDescriptor desc = getPropertyDescriptor(targetType, name); + if(desc == null || desc.getter == null) { + throw new RuntimeException("No Gettable Property By Name " + name + " Found In Class " + targetType.getName()); + } + + Object ret = null; + + try { + ret = desc.getter.invoke(targetObject); + } catch (Exception e) { + e.printStackTrace(); + } + + return ret; + } + +} diff --git a/library/src/main/java/org/dbasu/robomvvm/util/ObjectTagger.java b/library/src/main/java/org/dbasu/robomvvm/util/ObjectTagger.java new file mode 100644 index 0000000..bd5f70e --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/util/ObjectTagger.java @@ -0,0 +1,130 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.util; + +import java.util.HashMap; +import java.util.WeakHashMap; + +/** + * Associates arbitrary tags with a supplied object. The tags are indexed using a {@link java.lang.String} key. Stores weak references to the supplied + * objects to allow them to be garbage collected. Automatically removes associated tags when its object is garbage collected. + */ +public class ObjectTagger { + + private static class Tag { + + private HashMap tags = new HashMap(); + + void addTag(String key, Object value) { + tags.put(key, value); + } + + Object removeTag(String key) { + return tags.remove(key); + } + + Object get(String key) { + return tags.get(key); + } + + boolean isEmpty() { + return tags.isEmpty(); + } + } + + private static WeakHashMap tagMap = new WeakHashMap(); + + /** + * Set a tag associated with an object. + * @param object + * The object to set the tag for. + * @param key + * The {@link java.lang.String} index for the tag. + * @param value + * The actual tag. + */ + public static synchronized void setTag(Object object, String key, Object value) { + + Tag tag = tagMap.get(object); + + if(tag == null) { + tag = new Tag(); + tagMap.put(object, tag); + } + + tag.addTag(key, value); + } + + /** + * Removes a tag associated with an object. + * @param object + * The object to remove the tag from. + * @param key + * The {@link java.lang.String} index of the tag to remove. + * @return + * The tag that has been removed. Null if no such tag exists. + */ + public static synchronized Object removeTag(Object object, String key) { + Tag tag = tagMap.get(object); + + if(tag == null) { + return null; + } + + Object ret = tag.removeTag(key); + + if(tag.isEmpty()) { + tagMap.remove(object); + } + + return ret; + } + + /** + * Gets a tag associated with an object. + * @param object + * The object to get the tag of. + * @param key + * The {@link java.lang.String} index of the tag to get. + * @return + * The associated tag. Null if no such tag exists. + */ + public static synchronized Object getTag(Object object, String key) { + Tag tag = tagMap.get(object); + + if(tag == null) { + return null; + } + + return tag.get(key); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/util/ThreadUtil.java b/library/src/main/java/org/dbasu/robomvvm/util/ThreadUtil.java new file mode 100644 index 0000000..29b86ee --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/util/ThreadUtil.java @@ -0,0 +1,49 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.util; + +import android.os.Looper; + +/** + * Utility functions for validating threads. + */ +public class ThreadUtil { + + /** + * Checks whether the current thread is the UI thread. + * @return + * True if the current thread is the UI thread. False otherwise. + */ + public static boolean isUiThread() { + return Looper.getMainLooper().getThread().equals(Thread.currentThread()); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/viewmodel/BaseViewModel.java b/library/src/main/java/org/dbasu/robomvvm/viewmodel/BaseViewModel.java new file mode 100644 index 0000000..2d34c64 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/viewmodel/BaseViewModel.java @@ -0,0 +1,168 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.viewmodel; + +import android.content.Context; + +import com.google.common.base.Preconditions; + +import org.dbasu.robomvvm.annotation.SetLayout; +import org.dbasu.robomvvm.binding.BindMode; +import org.dbasu.robomvvm.binding.Binding; +import org.dbasu.robomvvm.binding.DefaultValueConverter; +import org.dbasu.robomvvm.binding.ValueConverter; +import org.dbasu.robomvvm.componentmodel.Component; +import org.dbasu.robomvvm.componentmodel.EventArg; + +/** + * Abstract base class for view models. All view models are associated with a context + * and must contain a {@link org.dbasu.robomvvm.annotation.SetLayout} annotation specifying the resource id to use + * for its layout. + */ +public abstract class BaseViewModel extends Component { + + + protected final Context context; + + + /** + * Construct a BaseViewModel from a context. + * @param context + * The supplied context. + */ + public BaseViewModel(Context context) { + this.context = Preconditions.checkNotNull(context); + } + + + /** + * Get the associated context. + * @return + * The associated context. + */ + public Context getContext() { + return context; + } + + /** + * Get the layout resource id associated with this view model. The id is + * set using the {@link org.dbasu.robomvvm.annotation.SetLayout} annotation. + * @return + * The associated resource Id. + */ + public int getLayoutId() { + Class thisClass = this.getClass(); + + if(!thisClass.isAnnotationPresent(SetLayout.class)) { + throw new RuntimeException("Missing InjectLayout annotation in " + thisClass.getName()); + } + return ((SetLayout) thisClass.getAnnotation(SetLayout.class)).value(); + } + + + /** + * Override this in subclasses to set up the bindings between the view model and its layout. + */ + protected void bind() { + + } + + /** + * Bind a property of this view model to a property of an element in its layout. + * @param property + * The property of the view model + * @param elementId + * The id of the target element. + * @param elementProperty + * The property of the target element. + * @param valueConverter + * The value converter to use for conversion. + * @param bindMode + * The bind mode to use. + * @return + * The created binding. + */ + protected abstract Binding bindProperty(String property, int elementId, String elementProperty, ValueConverter valueConverter, BindMode bindMode); + + + /** + * Binds an event of this view model to an action of an element in its layout. + * @param elementId + * The id of the target element. + * @param eventType + * The event arg class to bind. + * @param action + * The action of the target element. + * @return + * The created binding. + */ + protected abstract Binding bindAction(int elementId, Class eventType, String action); + + + /** + * Bind a property of this view model to a property of an element in its layout. Performs no conversion. + * Uses {@link org.dbasu.robomvvm.binding.BindMode#SOURCE_TO_TARGET} which results in one-way + * binding from this view model to the target element. + * + * @param property + * The property of the view model + * @param elementId + * The id of the target element. + * @param elementProperty + * The property of the target element. + * @return + * The created binding. + */ + protected final Binding bindProperty(String property, int elementId, String elementProperty) { + + return bindProperty(property, elementId, elementProperty, new DefaultValueConverter(), BindMode.SOURCE_TO_TARGET); + } + + + /** + * Bind a property of this view model to a property of an element in its layout. Performs no conversion. + * @param property + * The property of the view model + * @param elementId + * The id of the target element. + * @param elementProperty + * The property of the target element. + * @param bindMode + * The bind mode to use. + * @return + * The created binding. + */ + protected final Binding bindProperty(String property, int elementId, String elementProperty, BindMode bindMode) { + + return bindProperty(property, elementId, elementProperty, new DefaultValueConverter(), bindMode); + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/viewmodel/ItemCheckedEventArg.java b/library/src/main/java/org/dbasu/robomvvm/viewmodel/ItemCheckedEventArg.java new file mode 100644 index 0000000..7f16a1e --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/viewmodel/ItemCheckedEventArg.java @@ -0,0 +1,78 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.viewmodel; + +import org.dbasu.robomvvm.componentmodel.Component; +import org.dbasu.robomvvm.componentmodel.EventArg; + +/** + * Raised by {@link org.dbasu.robomvvm.viewmodel.ViewModelCollection} to notify about the checked + * status of its items. + */ +public class ItemCheckedEventArg extends EventArg { + + private final boolean value; + private final int position; + + /** + * Construct an ItemCheckedEventArg. + * @param source + * The event source component belonging to the source view model collection. + * @param position + * The position at which this applies. + * @param value + * The checked status at the supplied position. + */ + public ItemCheckedEventArg(Component source, int position, boolean value) { + super(source); + this.position = position; + this.value = value; + } + + /** + * Gets the position of the item whose checked status has changed. + * @return + * the position whose checked status has changed. + */ + public int getPosition() { + return position; + } + + /** + * Gets the checked status of the item at the specified position. + * @return + * Checked status. + */ + public boolean getValue() { + return value; + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/viewmodel/MenuViewModel.java b/library/src/main/java/org/dbasu/robomvvm/viewmodel/MenuViewModel.java new file mode 100644 index 0000000..9960e77 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/viewmodel/MenuViewModel.java @@ -0,0 +1,172 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.viewmodel; + +import android.content.Context; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import com.google.common.base.Preconditions; + +import org.dbasu.robomvvm.binding.BindMode; +import org.dbasu.robomvvm.binding.Binding; +import org.dbasu.robomvvm.binding.ValueConverter; +import org.dbasu.robomvvm.componentmodel.ComponentAdapter; +import org.dbasu.robomvvm.componentmodel.EventArg; +import org.dbasu.robomvvm.util.ObjectTagger; +import org.dbasu.robomvvm.util.ThreadUtil; + +/** + * View model used for creating and binding menus. + */ +public class MenuViewModel extends BaseViewModel { + + private static final String VIEW_MODEL = "robomvvm_view_model"; + + private Menu menu = null; + + /** + * Construct a MenuViewModel with a supplied context. + * @param context + * The supplied context. + */ + public MenuViewModel(Context context) { + super(context); + } + + + private int getNumItems(Menu menu) { + int ret = 0; + + while (true) { + try { + menu.getItem(ret); + } catch (IndexOutOfBoundsException e) { + return ret; + } + + ret ++; + } + } + + + private MenuItem getItemAt(Menu menu, int index) { + try { + return menu.getItem(index); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + + /** + * Inflate the menu corresponding to this view model. + * @param menu + * The menu to inflate to. + */ + public void inflate(Menu menu) { + + int currNumItems = getNumItems(menu); + MenuInflater inflater = new MenuInflater(context); + int menuId = getLayoutId(); + + inflater.inflate(menuId, menu); + + MenuItem root = getItemAt(menu, currNumItems); + + if(root != null) { + ObjectTagger.setTag(root, VIEW_MODEL, this); + this.menu = menu; + bind(); + this.menu = null; + } + } + + + /** + * Bind a property of this view model to a property of a menu item in its layout. + * @param property + * The property of the view model + * @param menuId + * The id of the target menu item. + * @param menuProperty + * The property of the target menu item. + * @param valueConverter + * The value converter to use for conversion. + * @param bindMode + * The bind mode to use. + * @return + * The created binding. + */ + @Override + protected final Binding bindProperty(String property, int menuId, String menuProperty, ValueConverter valueConverter, BindMode bindMode) { + + Preconditions.checkArgument(ThreadUtil.isUiThread(), "MenuViewModel.bindProperty can only be called from the UI thread"); + Preconditions.checkNotNull(property); + Preconditions.checkNotNull(menuProperty); + Preconditions.checkNotNull(valueConverter); + Preconditions.checkNotNull(bindMode); + + MenuItem targetItem = menu.findItem(menuId); + ComponentAdapter adapter = ComponentAdapter.get(targetItem); + Binding binding = Binding.bindProperty(this, property, adapter, menuProperty, valueConverter, bindMode); + + return binding; + } + + + /** + * Binds an event of this view model to an action of a menu item in its layout. + * @param menuId + * The id of the target menu item. + * @param eventType + * The event arg class to bind. + * @param action + * The action of the target menu item. + * @return + * The created binding. + */ + @Override + protected Binding bindAction(int menuId, Class eventType, String action) { + + Preconditions.checkArgument(ThreadUtil.isUiThread(), "MenuViewModel.bindAction can only be called from the UI thread"); + Preconditions.checkNotNull(eventType); + Preconditions.checkNotNull(action); + + MenuItem targetItem = menu.findItem(menuId); + ComponentAdapter adapter = ComponentAdapter.get(targetItem); + Binding binding = Binding.bindAction(adapter, this, eventType, action); + + return binding; + } +} diff --git a/library/src/main/java/org/dbasu/robomvvm/viewmodel/ViewModel.java b/library/src/main/java/org/dbasu/robomvvm/viewmodel/ViewModel.java new file mode 100644 index 0000000..05e779e --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/viewmodel/ViewModel.java @@ -0,0 +1,205 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.viewmodel; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.google.common.base.Preconditions; + +import org.dbasu.robomvvm.binding.BindMode; +import org.dbasu.robomvvm.binding.Binding; +import org.dbasu.robomvvm.binding.ValueConverter; +import org.dbasu.robomvvm.componentmodel.ComponentAdapter; +import org.dbasu.robomvvm.componentmodel.EventArg; +import org.dbasu.robomvvm.util.ObjectTagger; +import org.dbasu.robomvvm.util.ThreadUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * View model used for creating and binding views. + */ +public class ViewModel extends BaseViewModel { + + private static final String VIEW_MODEL = "robomvvm_view_model"; + private static final String VIEW_BINDINGS = "robomvvm_view_bindings"; + + private View view = null; + + private final List bindings = new ArrayList(); + + /** + * Construct a ViewModel with a supplied context. + * @param context + * The supplied context. + */ + public ViewModel(Context context) { + super(context); + } + + + /** + * Attempt to unbind a pre-existing view from its view model and bind it to this + * view model. + * @param viewToConvert + * The view to convert. + * @return + * Null if viewToConvert is null. Null if viewToConvert corresponds to a different view model class. + * The bound view otherwise. + */ + public View convertView(View viewToConvert) { + + Preconditions.checkArgument(ThreadUtil.isUiThread(), "ViewModel.convertView can only be called from the UI thread"); + + if(viewToConvert == null) return null; + + ViewModel otherViewModel = (ViewModel) ObjectTagger.getTag(viewToConvert, VIEW_MODEL); + + + if(otherViewModel == null || !otherViewModel.getClass().equals(this.getClass())) return null; + + + List otherViewBindings = (List) ObjectTagger.getTag(viewToConvert, VIEW_BINDINGS); + + if(otherViewBindings != null) { + + for (Binding binding : otherViewBindings) { + binding.unbind(); + } + } + + this.view = viewToConvert; + bind(); + + ObjectTagger.setTag(viewToConvert, VIEW_MODEL, this); + ObjectTagger.setTag(viewToConvert, VIEW_BINDINGS, new ArrayList(bindings)); + this.view = null; + bindings.clear(); + return viewToConvert; + } + + + /** + * Create a view corresponding to this view model. Created with a null parent. + * @return + * The created view. + */ + public View getView() { + return getView(null); + } + + + /** + * Create a view corresponding to this view model. + * @param parent + * Parent to attach the created view to. + * @return + * The created view. + */ + public View getView(ViewGroup parent) { + + Preconditions.checkArgument(ThreadUtil.isUiThread(), "ViewModel.getView can only be called from the UI thread"); + + int layoutId = getLayoutId(); + + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View viewToConvert = inflater.inflate(layoutId, parent, false); + + ObjectTagger.setTag(viewToConvert, VIEW_MODEL, this); + + return convertView(viewToConvert); + } + + + /** + * Bind a property of this view model to a property of a view in its layout. + * @param property + * The property of the view model + * @param viewId + * The id of the target view. + * @param viewProperty + * The property of the target view. + * @param valueConverter + * The value converter to use for conversion. + * @param bindMode + * The bind mode to use. + * @return + * The created binding. + */ + @Override + protected final Binding bindProperty(String property, int viewId, String viewProperty, ValueConverter valueConverter, BindMode bindMode) { + + Preconditions.checkArgument(ThreadUtil.isUiThread(), "ViewModel.bindProperty can only be called from the UI thread"); + Preconditions.checkNotNull(property); + Preconditions.checkNotNull(viewProperty); + Preconditions.checkNotNull(valueConverter); + Preconditions.checkNotNull(bindMode); + + View targetView = view.findViewById(viewId); + ComponentAdapter adapter = ComponentAdapter.get(targetView); + Binding binding = Binding.bindProperty(this, property, adapter, viewProperty, valueConverter, bindMode); + bindings.add(binding); + + return binding; + } + + + /** + * Binds an event of this view model to an action of a view in its layout. + * @param viewId + * The id of the target view. + * @param eventType + * The event arg class to bind. + * @param action + * The action of the target view. + * @return + * The created binding. + */ + @Override + protected Binding bindAction(int viewId, Class eventType, String action) { + + Preconditions.checkArgument(ThreadUtil.isUiThread(), "ViewModel.bindAction can only be called from the UI thread"); + Preconditions.checkNotNull(eventType); + Preconditions.checkNotNull(action); + + View targetView = view.findViewById(viewId); + ComponentAdapter adapter = ComponentAdapter.get(targetView); + Binding binding = Binding.bindAction(adapter, this, eventType, action); + bindings.add(binding); + return binding; + } + +} diff --git a/library/src/main/java/org/dbasu/robomvvm/viewmodel/ViewModelCollection.java b/library/src/main/java/org/dbasu/robomvvm/viewmodel/ViewModelCollection.java new file mode 100644 index 0000000..0bc7d04 --- /dev/null +++ b/library/src/main/java/org/dbasu/robomvvm/viewmodel/ViewModelCollection.java @@ -0,0 +1,293 @@ +/** + * @project RoboMVVM + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.viewmodel; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; + +import com.google.common.base.Preconditions; +import com.google.common.primitives.Ints; + +import org.dbasu.robomvvm.componentmodel.Component; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * A collection of {@link org.dbasu.robomvvm.viewmodel.ViewModel}s. Can be used as an array adapter in adapter views. + * Has an event source {@link org.dbasu.robomvvm.componentmodel.Component} to notify listeners of changing item checked states + * using {@link org.dbasu.robomvvm.viewmodel.ItemCheckedEventArg}. + */ +public class ViewModelCollection extends ArrayAdapter { + + private int selectedItem = -1; + private final List checkedItems = new ArrayList(); + private final Component eventSource = new Component(); + + + /** + * Get the event source component that belongs to this view model collection. + * @return + * The event source component. + */ + public Component getEventSource() { + return eventSource; + } + + + /** + * Construct a ViewModelCollection with a supplied context using an internal + * list of view models as a backing store. + * + * @param context + * The supplied context. + */ + public ViewModelCollection(Context context) { + super(context, 0); + } + + /** + * Construct a ViewModelCollection with a supplied context using a supplied list of view models + * as a backing store. + * + * @param context + * The supplied context. + * @param objects + * The list of view models to use as a backing store. + */ + public ViewModelCollection(Context context, List objects) { + super(context, 0, objects); + } + + + /** + * Remove all the items contained in a collection from this view model collection. + * @param items + * A collection representing the items to remove from this view model collection. + */ + public void removeAll(Collection items) { + + Preconditions.checkNotNull(items); + for(T item : items) { + remove(item); + } + } + + /** + * Remove all the items in the argument list from this view model collection. + * @param items + * The items to remove from this view model collection. + */ + public void removeAll(T... items) { + removeAll(Arrays.asList(items)); + } + + + private View getCustomView(int position, View convertView, ViewGroup parent) { + T viewModel = this.getItem(position); + + View view = viewModel.convertView(convertView); + if(view != null) return view; + + return viewModel.getView(parent); + + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + + return getCustomView(position, convertView, parent); + } + + @Override + public View getDropDownView (int position, View convertView, ViewGroup parent) { + + return getCustomView(position, convertView, parent); + } + + /** + * Returns a list of positions of checked items in this view model collection. + * @return + * A list of positions of checked items in this view model collection. + * Empty list if nothing is checked. + */ + public List getCheckedItems() { + return new ArrayList(checkedItems); + } + + /** + * Checks if an item at the given position is checked. + * @param position + * The position of the item. + * @return + * True if the item at the given position is checked. False otherwise. + */ + public boolean isItemChecked(int position) { + + return checkedItems.contains(Integer.valueOf(position)); + } + + /** + * Remove the item at the given position from the list of checked items. + * @param position + * The item position. + * @return + * True if the item at the given position was checked. False otherwise. + */ + public boolean removeCheckedItem(int position) { + + boolean removed = checkedItems.remove(Integer.valueOf(position)); + + if(removed) { + eventSource.raiseEvent(new ItemCheckedEventArg(eventSource, position, false)); + } + + return removed; + } + + /** + * Remove all the the items at positions given by a collection from the list of checked items. + * @param positions + * A collection representing the positions of the checked items to remove. + * @return + * True is any of the items given by the positions collection were checked. + * False otherwise. + */ + public boolean removeCheckedItems(Collection positions) { + + Preconditions.checkNotNull(positions); + boolean ret = false; + + for(Integer position : positions) { + boolean removed = removeCheckedItem(position); + ret |= removed; + } + + return ret; + + } + + /** + * Gets the position of the selected item. + * @return + * The position of the selected item. + */ + public int getSelectedItem() { + return selectedItem; + } + + + /** + * Remove all the the items at positions given by the argument list from the list of checked items. + * @param positions + * The positions of the checked items to remove. + * @return + * True is any of the items given by the positions were checked. + * False otherwise. + */ + public boolean removeCheckedItems(int... positions) { + return removeCheckedItems(Ints.asList(positions)); + } + + /** + * Add the item at a give position to the list of checked items. + * @param position + * The item position. + */ + public void addCheckedItem(int position) { + + if(!isItemChecked(position)) { + checkedItems.add(position); + eventSource.raiseEvent(new ItemCheckedEventArg(eventSource, position, true)); + } + } + + /** + * Add all the items at positions given by a collection to the list of checked items. + * @param positions + * A collection representing the item positions. + */ + public void addCheckedItems(Collection positions) { + + Preconditions.checkNotNull(positions); + + for(Integer position : positions) { + addCheckedItem(position); + } + } + + /** + * Add all the items at positions given by the argument list to the list of checked items. + * @param positions + * The item positions. + */ + public void addCheckedItems(int... positions) { + addCheckedItems(Ints.asList(positions)); + } + + /** + * Set the item at a give position as the only checked item. Remove any items that were previously checked + * from the list of checked items. + * @param position + * The item position. + */ + public void setCheckedItem(int position) { + clearCheckedItems(); + checkedItems.add(position); + eventSource.raiseEvent(new ItemCheckedEventArg(eventSource, position, true)); + } + + /** + * Clear all items from the the list of checked items. + */ + public void clearCheckedItems() { + for(Integer i : checkedItems) { + eventSource.raiseEvent(new ItemCheckedEventArg(eventSource, i, false)); + } + checkedItems.clear(); + } + + /** + * Gets the most recently checked item. + * @return + * Most recently checked item. -1 if nothing is checked. + */ + public int getCheckedItem() { + int index = checkedItems.size() -1; + return index == -1? -1 : checkedItems.get(index); + } + +} diff --git a/sample_itemlist/.gitignore b/sample_itemlist/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/sample_itemlist/.gitignore @@ -0,0 +1 @@ +/build diff --git a/sample_itemlist/build.gradle b/sample_itemlist/build.gradle new file mode 100644 index 0000000..ff16b3d --- /dev/null +++ b/sample_itemlist/build.gradle @@ -0,0 +1,25 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 18 + buildToolsVersion "20.0.0" + + defaultConfig { + applicationId "org.dbasu.robomvvm.sample" + minSdkVersion 15 + targetSdkVersion 18 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + runProguard false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile project(':library') +} diff --git a/sample_itemlist/proguard-rules.pro b/sample_itemlist/proguard-rules.pro new file mode 100644 index 0000000..9bae883 --- /dev/null +++ b/sample_itemlist/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:/Program Files (x86)/Android/android-studio/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class propertyName to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/sample_itemlist/src/main/AndroidManifest.xml b/sample_itemlist/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d7756dc --- /dev/null +++ b/sample_itemlist/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/AboutPopupViewModel.java b/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/AboutPopupViewModel.java new file mode 100644 index 0000000..06830b9 --- /dev/null +++ b/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/AboutPopupViewModel.java @@ -0,0 +1,59 @@ +/** + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.sample_itemlist; + +import android.content.Context; + +import org.dbasu.robomvvm.annotation.SetLayout; +import org.dbasu.robomvvm.componentadapter.view.ClickEventArg; +import org.dbasu.robomvvm.viewmodel.ViewModel; + + +/** + * View model for the about popup. + */ +@SetLayout(R.layout.popup_about) +public class AboutPopupViewModel extends ViewModel { + + public AboutPopupViewModel(Context context) { + super(context); + } + + public void hidePopup() { + MainActivity mainActivity = (MainActivity) getContext(); + mainActivity.hideAboutPopup(); + } + + @Override + protected void bind() { + bindAction(R.id.about_popup_ok_button, ClickEventArg.class, "hidePopup"); + } +} diff --git a/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/MainActivity.java b/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/MainActivity.java new file mode 100644 index 0000000..1df1592 --- /dev/null +++ b/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/MainActivity.java @@ -0,0 +1,93 @@ +/** + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.dbasu.robomvvm.sample_itemlist; + +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.widget.PopupWindow; + + +/** + * Main activity for a basic use case of RoboMVVM(https://github.com/debdattabasu/RoboMVVM). This app lets you add, + * remove and modify string items in a ListView. It also has an options menu where you can view a description of this app. + */ +public class MainActivity extends Activity { + + private PopupWindow aboutPopup; + private View rootView; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + /** + * Set MainActivityViewModel as the view model of this + * activity. + */ + MainActivityViewModel viewModel = new MainActivityViewModel(this); + rootView = viewModel.getView(); + setContentView(rootView); + + /** + * Set AboutPopupViewModel as the view model for + * the popup window. + */ + + AboutPopupViewModel aboutPopupViewModel = new AboutPopupViewModel(this); + aboutPopup = new PopupWindow(aboutPopupViewModel.getView(), LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + + /** + * Set MainMenuViewModel as the view model + * for the options menu. + */ + MainMenuViewModel mainMenu = new MainMenuViewModel(this); + mainMenu.inflate(menu); + return true; + } + + public void showAboutPopup() { + aboutPopup.showAtLocation(rootView, Gravity.CENTER, 0, 0); + } + + public void hideAboutPopup() { + aboutPopup.dismiss(); + } +} diff --git a/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/MainActivityViewModel.java b/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/MainActivityViewModel.java new file mode 100644 index 0000000..a31d40c --- /dev/null +++ b/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/MainActivityViewModel.java @@ -0,0 +1,148 @@ +/** + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.sample_itemlist; + + +import android.content.Context; + +import org.dbasu.robomvvm.annotation.SetLayout; +import org.dbasu.robomvvm.binding.BindMode; +import org.dbasu.robomvvm.viewmodel.ViewModel; +import org.dbasu.robomvvm.viewmodel.ViewModelCollection; +import org.dbasu.robomvvm.binding.TypedValueConverter; +import org.dbasu.robomvvm.componentadapter.view.ClickEventArg; +import org.dbasu.robomvvm.componentadapter.listview.ItemClickEventArg; +import org.dbasu.robomvvm.componentadapter.listview.ItemLongClickEventArg; +import java.util.ArrayList; +import java.util.List; + +/** + * View model for the main activity. + */ +@SetLayout(R.layout.activity_main) +public class MainActivityViewModel extends ViewModel { + + private final ViewModelCollection strings = new ViewModelCollection(getContext()); + private String text = "Hello World"; + + private final StringViewModel emptyViewModel = new StringViewModel(getContext(), "List is empty. Click Add to add items."); + + + public StringViewModel getEmptyViewModel() { + return emptyViewModel; + } + + public MainActivityViewModel(Context context) { + super(context); + } + + + public ViewModelCollection getStrings() { + return strings; + } + + public String getText() { + return text; + } + + public void setText(String text) { + + this.text = text; + raisePropertyChangeEvent("text"); + } + + public void itemClick(ItemClickEventArg arg) { + + if(strings.isItemChecked(arg.getPosition())) { + strings.removeCheckedItem(arg.getPosition()); + } else { + strings.setCheckedItem(arg.getPosition()); + } + setText(strings.getItem(arg.getPosition()).getString()); + } + + public void itemLongClick(ItemLongClickEventArg arg) { + + if(strings.isItemChecked(arg.getPosition())) { + strings.removeCheckedItem(arg.getPosition()); + } else { + strings.addCheckedItem(arg.getPosition()); + } + + setText(strings.getItem(arg.getPosition()).getString()); + } + + public void addItem() { + + strings.add(new StringViewModel(getContext(), text)); + setText(strings.getItem(strings.getCount() - 1).getString()); + strings.setCheckedItem(strings.getCount() - 1); + + } + + public void deleteItem() { + + List dead = new ArrayList(); + + for(Integer i : strings.getCheckedItems()) { + dead.add(strings.getItem(i)); + } + + strings.removeAll(dead); + strings.clearCheckedItems(); + + } + + public void modifyItem() { + + for(Integer i : strings.getCheckedItems()) { + strings.getItem(i).setString(text); + } + } + + @Override + public void bind() { + + /** + * Bind properties and actions. + */ + bindAction(R.id.delete_button, ClickEventArg.class, "deleteItem"); + bindAction(R.id.add_button, ClickEventArg.class, "addItem"); + bindAction(R.id.modify_button, ClickEventArg.class, "modifyItem"); + bindAction(R.id.list_view, ItemClickEventArg.class, "itemClick"); + bindAction(R.id.list_view, ItemLongClickEventArg.class, "itemLongClick"); + + + bindProperty("text", R.id.text_main, "text", new TypedValueConverter(String.class, CharSequence.class), BindMode.BIDIRECTIONAL); + bindProperty("strings", R.id.list_view, "source"); + bindProperty("emptyViewModel", R.id.list_view, "emptyViewModel"); + } +} diff --git a/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/MainMenuViewModel.java b/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/MainMenuViewModel.java new file mode 100644 index 0000000..eeec4b2 --- /dev/null +++ b/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/MainMenuViewModel.java @@ -0,0 +1,63 @@ +/** + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.sample_itemlist; + +import android.content.Context; + +import org.dbasu.robomvvm.annotation.SetLayout; +import org.dbasu.robomvvm.componentadapter.menuitem.MenuItemClickEventArg; +import org.dbasu.robomvvm.viewmodel.MenuViewModel; + +/** + * View model for the main menu. + */ +@SetLayout(R.menu.main) +public class MainMenuViewModel extends MenuViewModel { + + public MainMenuViewModel(Context context) { + super(context); + } + + public void showAboutPopup() { + MainActivity mainActivity = (MainActivity) getContext(); + mainActivity.showAboutPopup(); + } + + + @Override + protected void bind() { + + bindAction(R.id.action_about, MenuItemClickEventArg.class, "showAboutPopup"); + + } + + +} diff --git a/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/StringViewModel.java b/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/StringViewModel.java new file mode 100644 index 0000000..a3068fc --- /dev/null +++ b/sample_itemlist/src/main/java/org/dbasu/robomvvm/sample_itemlist/StringViewModel.java @@ -0,0 +1,65 @@ +/** + * @project RoboMVVM(https://github.com/debdattabasu/RoboMVVM) + * @author Debdatta Basu + * + * @license 3-clause BSD license(http://opensource.org/licenses/BSD-3-Clause). + * Copyright (c) 2014, Debdatta Basu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.dbasu.robomvvm.sample_itemlist; + +import android.content.Context; + +import org.dbasu.robomvvm.annotation.SetLayout; +import org.dbasu.robomvvm.viewmodel.ViewModel; + +/** + * View model for strings used to populate a List View in {@link org.dbasu.robomvvm.sample_itemlist.MainActivityViewModel}. + */ + +@SetLayout(R.layout.string_view) +public class StringViewModel extends ViewModel { + + public StringViewModel(Context context, String string) { + super(context); + this.string = string; + } + + private String string; + + public void setString(String value) { + this.string = value; + raisePropertyChangeEvent("string"); + } + + public String getString() { + return string; + } + + @Override + public void bind() { + bindProperty("string", R.id.text_string, "text");; + } +} diff --git a/sample_itemlist/src/main/res/drawable-hdpi/ic_launcher.png b/sample_itemlist/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..96a442e5b8e9394ccf50bab9988cb2316026245d GIT binary patch literal 9397 zcmV;mBud+fP)L`9r|n3#ts(U@pVoQ)(ZPc(6i z8k}N`MvWQ78F(rhG(?6FnFXYo>28{yZ}%O}TvdDT_5P?j=iW=V`8=UNc_}`JbG!ST zs@lK(TWkH+P**sB$A`cEY%Y53cQ}1&6`x-M$Cz&{o9bLU^M-%^mY?+vedlvt$RT-^ zu|w7}IaWaljBq#|I%Mpo!Wc2bbZF3KF9|D%wZe{YFM=hJAv$>j>nhx`=Wis#KG!cJA5x!4)f) zezMz1?Vn$GnZNjbFXH(pK83nn!^3=+^*kTTs5rV9Dq^XS(IKO!mKt5!dSmb3IVCxZ z8TTk5IE)F1V29$G7v#j9d-hy&_pdg8?kT4)zqr>?`}I%W>(?GO%*C&}?Fp|bI*~2&KZ$%^B6R&1~2kA{`CWy+>F-x=z-f{_&vyu_3yp{jtw(*syi% zu3t2|4{c~LJXRt2m>rMg2V_kLltCZ<`m>qcI?BPP?6hf``|e!rZEFszeYQ3f-*nAS zZ+h1$mFwy+7156lkB(k6)!1fUbJCxgIBK38$jj5cC$r&YXN)nr#PY=tJaLc?C_o?j+8H3Q>891JJ9&$l-r+-SG#q)*;r52% z@nlKflb65o%s*Jt)!pw1k{vIoQIvoJ0Y&Msiw0X!qJ)_47G*?aJ6bJFLh_4b$5&1k5wN>du*>6#i7R9T8; z7>EHOV=ue7mo77SJPwER4(A+s?n0JjYK)b}Om6n>ke?0JR=jTI+RFBg_iwb7k%n*2 zR_M0DJ9x+0zxba4(B1y^JQ_Nj6dlP5PGXvSq8fF#mxrFYj3d9(V#jJwt+IqU9+8+D z6C6Us1OI$d8OF!3+Hm1 zW5in zXV^%U35HooOpSmeqlG6e0kUMYNonKp1vr|My9}4-WO+uOxe_c-o&}%voNYHkqtle% z5yQ_^oozSUUNu30EQSAl!Q%(%3G1NXENSMjCL*Vx-Td2~rk(}d z8pT!HZe>1r5EGuz`pgsg@^yQEi=BIa#meLq0!?{TZ}q#}=7UC9_l=w|wv+pP!g4#! zRys6EN$Jv}#U47$k&)pDzvks}LGfPku6P9p!56Py)~1)W(11n7n}`Wx!=;_JTiu#d zpCqx=hEk@t4sp?!j{W}wP@V-=Pd=T^>6IKBy;#mLA7hCe{V7B3@I7Ipa}L`MbF|YQ z)$BNWsiEnoNHrtJli|n8cOnn4NyF=8MbVxgof0>Uv%wM_j94a;8(LMjlL~E(99gJ*2%JtNtAkD@j;^ za~Y~&j6uY{=Rv5S4joH*RW_m9N{ZSN0HhAwFyJNok zS9kx$>wMf%tUi&Eb`6u0lWJ|k?A-42(lp2UmS(PrAc(24wexRiHUieMwf$o%m6$xs zp#-SdBUu2D5`v;(9-sm&kN2M74c&AvKe_v@tQ|dzJ2qSgQHpnUP(iQ?J%Il;Jdyp# z7}cpq6Kdm+FS~zS4Eo;fuO=DFP*UlpO|_CNt5&NUqBvQWxmg7#ARvMf=%#H@p%RZ` zjK$hMbNb+vVP3UlkfIt&ptJ<00Ic{Ka+lF+&w;OEs1O2#V8~O|R*Gq9TIgM&UqM&bZOXBwnbC? zDr))NR&g>lwVgcmnx`K1$)PTTw3m}-T11^ZkY{}jQ@lGD$XzJIcVFkYBBW=o_}TUU zt@yd{Jz;@~72x#!RG(#ira6}v-*J#<{@@^OI-Q2T^}=IKLubsa&V-%WwlF1s7fz~u zMdQTV7SnRet#^`VO0V7H(?59X{uy+S`(sorO@2-+qioUdo9+6r4#|jb=?t50oh42R z{}I>Krut|YKkOc|O|M>y#(3YA;I(i+MiHSfwbJA$jIUr$Y2i|u)*>@2eUYk`j4C5r z>61dKu!AqM_E7#DoDzbd-bfT%AYXUUB{SS|{b{`5^?wz1{PVQgTlvyqOX8(#GTz(U zNPhnj>$lC`xaD56`TjW&uW8p~qikP*F8kHFM0frzdk%UNGjb1O$%uLK`0-)2UsZ3L z#+j+CI_8k4VslL%$aVR@joX>M-@odbX!os$xY$HDIOCokY?{Q0v2kQErf|ZlN>D9w zC+2}E&?rDdi#%))$p%P4C_xGXu=@U~_<|V4L|{>TP$XBp$5pCPXLzK3!;gP>7=QNi zkNOur`>xY=@VSpB#LsN9JKpOz({ANcdv>?K+D_*_HZ<;9>kplj^Ph5!e&&a#?(3vK z_Q@}D_M5kGcx^AuaI~qKYUnb1Mj-n;MURXa)+x7~e2gbMW|gw?5Rg zTOMlo>6zIJ$VNVgn(@kTSL0eP)nR35IHpoHM2W#h6cNmTm@-9`dFJ$;k(S`7Lg@RY zp!hNmb9un!O4Wt05ANDGirv(B14gW| zwjP}C9bK{J`qZ_S2o)b`RonR-b8~y8)$H0`+gg6>#^wu8eCp9xA9B>>8(KRizI?+^ zAJ#i>*({qM-c4gBB~5dzg(wj!HA`hkh!aDl5>u&J;>2K#Ax2)2wt|L!9X;(=*jy!`r4_FhCBoRxNjXNv(~jGQ|%<}%K6RimaBJcP0v}oCgRN3B;oiM)opj? zXm;;tv3q-yy}NqMOr^~3&1lW$w3}UK_IT2sCrkYx5$&6e2A%g;QZUX~A&L!2rFd0p z5%men@^zN_Xw2|v%*c2|wQfkN4r6u&k;LxYY+w3{KY#cie)!iz>(yAgt=&-+Sy2V& z9BJxI+VMKQ%dvY~x>gmEijj3ss_*NAT(8d1@DQ6e&#Ln&6Qk>wHrh>;V2nvomC`8& z(w?`?*_^3u-TJrMzv2~7dH(XLJvUOXk4U8oW6Ol)YsawhIB{GdvIzu1hzMTrE)cvB z%2GxMpaF89<9uF(?cfN(BNR?wwWvCZ6e62+G_{$+;`yjgLj{(^z*zzwd;K3RElb*%=??P zm+lLY0@Y}^kVdMYX5M)YJ~8h=i(S{q#NfU0xPTao4WPDQL=Y_;vg=p%iay1_`<0Ga zMG&<(pOU+bI2u9_g8IJBTqGX*3@G$Zc`pj0f@)vd2?Aj`ms>DHg>;w~p}HXV(*VJX zphd;fht9qL3E)D8h$$A;SGl22Ygv>`iU=A)z=1ZYN$|2`*$`R)?KD>$tw_e9h_x~eX_udS~Q%yz?48i*aIa+_wx|j{B zsG7mwZ)6M3dmvgMC3K-66;ML(9o2xU!F8+qF)>v{1;ip)6v_I)6law|rd_Dx2oV|n z(Qm_PUnTTuKFG)w%s|)lS!w~Lm$k|Al=0djocyHU;>1H=!N}0E0lSV^b2^6~^lUco zyoH+|_!li3#euHd4TJS8=CLaHG9H8g&h3Xm z#>BkpUBAmae(#)qO3)ZMG3irM=5IzA^s+)w86=tIMT{&?Awux<(k2>U#n`c&@Z?u= z%=#BoO-9Nc^?)hz*YW~~tU8rLR-MZBJsY_7fp2r~mY>q-O;L%5Fp?}V6CK=F(18U3 znxB8ZR0TT{)T64RDt!+yFgp!JXGP0|It0Hz2Em#YfRv>O>8A?J=Sz!nq<|{&mW=?~ zDQT{S6PH0|jwy37t+0Ob6izz)JdRlNEUbyk>-K?}FOT=Dj9SuS_0nTFd+A^D?Bo83 zTkicXcW=IuZoZd(Dl;&#`LI;_s?e;OH9quf?*XuV0O$Qh0j~HWKpA|PXV4&b2zs z@W5<)dtovIRZ@gvsi$^s;v05(XwF3$lJ;wzYfE`46fnT7>!qt|hWHRE>yQP)i8= zVbC|O{Ud6%kwGcch>>|pE-=?cW;TDR0lE5Nw7l66lr-zIYT3bj^ujCn$b0{ZO;gwK z#}}W(*T3~in$6ZCpbB98pftPTo;!K>U;H*7_}t4m;;4i9#^2t`pS<=jsnx198);d3 z-M6Mx{7-c0A-jhJQ`5mBy8TBnfbr2~sER5E5oz}=so34cg)GYarRWi8w#W$%G{?Z*4xDb#LX1B1 zg!4G{m~*)H_J8J^SNt`XU-fxjea`>p_$Qyn*Dn18*WdPCp8oWw^XU)%kfRQHMgfQh z1j_ua@O4G%QK;&YH3Y9(q!hkgOUCkcVH5N0Ug(EPX%H6qCfPqg))qrd#ec^47dBu- z=sRkmjGS>3K(tfRTo;zCXO-74hV;y1!vCN}v|w?AWR$YpYXs@Dr?iNLKD9s|2)0aHY!TKTYhwMI z7b#54h!H6rUU9+xnL$g6h?t?Li5guXPY1g)$bI$~rHWP%QkYJ6Y-U^0C(@*$ruN2*zn0QRBOeVpgMFbT%k!Dn1*u#%J^y)enX1K;0~ z%3Q zP(b%}P!Loj6M{v96(Qa~K!bq-V-P89U_K)0zHC_F#L==3IPh2hHG6&?rxvQ%|EljR zfGIDyu=rIrl1dyjuMfwuh?pXZmARwNZ?GbW;5BH5D#nN|WbGm+UGAh7_AcG>4&|{0 zrg?k@h8zm!0A|5Zo%X%g|2tBPKHHB6`~4h?I@bepDe6?^f8w zBnzfOf|j{kR5m6BLRr0$!RZ$PHSk*)tyjkws*DpyHIiiL*8o(Smx(OKT7@D&Y3OI^ zEUMtKa2*SLjt(eJsZsLsrgV`A+xL(~JN#JU6+L)gCe%VuSNbCzTr09w>eZ#779SKV z)m)@#TNVy|q3Tz_U`^7MY`l}`GU~OlQi|*cprX?tm@tIV+8kOGkaa=9Y<{N|RZ)ns zHlgnz2S%qwK9wXjest~Ux$YNNA{0?6Xpv{_mqYt8D`g&7Yb~>lX+HP&AK<=+Zl_kO z6a2g`^4=9W92GQ3e9Mk6?DlzlkIM`iOzwk*5L81TcuyYkI-<3^@49_+^XC7&N}SL1 zh$kIBxb`9+v}acfV?FQ zN#04eHe0*j{pz=zOj3#EHLrT3e)O;3xqpCWrl$e)PcD9jQ4P-8_zyZg^M7i|*kOuj znsvlwNUsy5+01^P_sqMOjXjxKwHn4)$87t-MWZZ*5Dbit4|D9vL+spsJ0JPd?{Ms) zFW^<@yqjZ=IvG%$ck_Cu9|b8CvoV%5P5IZWzs>i4`~`N+-p`7a6RbLHJ;nxtSB#Mb z`1I552=9DrYWFNZ{-=Mt;SVo5@3cmv`IZT@@>#~zCe-=qENxsn+uHfL`e?SbT3IQ_ zt~e)Lcirs_S5^X#?hDYmgV%8QQDe+?>*1&0e^BnaeZz(&D~3<)#QuUL8h*NlXgtr| z&a{_Z)o9FK_U5<0!E3N|yY1P2g%J9s*?!zF78+NSb%!ix)tbQ09oO&|U$~Bwk35^- zec9VN^xz{043e^xD}WEmzh8d^-~Pd8**bEfd+I?HuO~n4SksoN8LRPUy={E<@BjRMUh?X71Xaey>t^$&Eq2B7)u_r$ z|IQwpG52G!F$J5fRo1LqLB7iKz_!bI@27skX~+Eze|Y}IBuRp?hR7z|eA~7B<99#7 zrX4r2a_tCDUb_}Cg)g!OEVeJ5AEVRyb!9~f4OL68qhZZRP0l*>MdkxvxXeGWx$T>+ zI^X!wnYQDnwK9?i)j)eLXJU2Cw>~>R?72@MecvT7;h~2gATow_cbc)$Ws+xNSB{++ zo^tTp^y*(-Y-XF=$XyoBJnMN9+p!Qrep1)%ym_v7zZH{;u~L>T=4XP!f^?uC4ULUR zdl`>x+DVkHVd;|9#N*oubBFQEyRT#UK^0c7T}l)eEEFS)qvZl%f>#I;iCwAWb=kW0 z(e#lm51o?d>D|kgtTscVQCNDAXMAjxSX&{_Qf)T((wMHWWLbz6WpPXP0(3_SBWwI19Vx?$i6WUqP$4O|wjNbYzst$z{58`cBhm z&F(N-KeXFzo#aC|6BbC($As#B8X=}ggpDyQUp|Q>9cG$47#>TQn%T(eHA`5se7KnZ zF_dj_6NN0xS-oZ%Nj%PTpK=MC zw*4IMGls_v)mokI)Dph*pD<)7prEF|j6I$2=XF=Ua3z;BN^yt&H@G%7& zWnL7*e0S9svjSP>kuc;VCbZXUN3G7D8`G@!Qnjt=p=7yC?QH0tsa@RsuPMLj@wf-c z|LV)H$Auga+MTAU#>)eeuh_L`!qC=Ls|{m}Cy)|w6#aP}w6_-ya~9LF z{dQAPa-|&ME858gIK=}lVK7MLT~Oye&UM9y?0X=8Qmvb*)=X}iv%Me)Gqav+FWdGT zuk&#ak~?2Kzf}w)xZuKGx%+`1?Ecoq?*H@EjFm%C6OT577vWKoJB z$A^sIasm!5TGOFFGmHkKNTE7KW3nveUq1bt4Uj)!1_6BJ zU6=EoPrjVdk+pQX+j-GTpQS&&^43tT43kuRlvE8fGdYc!1|m)3WCuwlqB>NeQc0** zYE&wTj*QpuPLfJ)j2$(`sI@k@oR!^9d(3&Kd6r3*<)pooPNzq=)1%#NQ;nAsF*5VR zOYXQC;B^4*Sik--jy?J`uDj-! zSep}9YT4*SOrT2I6MF4H+EZFRPh+}^b4@i8OYk9Y&86o*Y4(`Ax1W4#tX^5m6LjZPb61LF2?qBy?B_?1YE!nej)R5c8qG`2s_uF`Cu+ z`X_$#2Ur#!Pw0WVd60fYG8A#y55LDyJ!Yt$5G6Efb<6Nr%-BTC_|llMB?%*A5%rOX z`fyBbD5g@4Ns^)P;F7zjv{t6u?k1J0kR*v#Dhair3iXjH^^qz=!xd`vm`W`oN-Wj_ zNML7~t!rRbc|9I0mUjpEgOJ9XGg2;vjDZ;b~V638P!uVuejytg~ci-I(n9#M6AR=mQG0YjoLKGPgFp(jS4Pn7UJR)Et z-8ZsqWsRLXri#f_BSeWIat3P+Q3Td1#ws={2CLGpDdvrgP#KD7 z&SnaR^#_Bsq;Xt;kyI^}iX~1WYzdHamc$tH1#Mz6f<2(WuH^s%^yXK78Gyg}{;LNA zoW%$)#R!a0wv&q%qj%+~i3^k&1jY!ljfi82Vr$~W5G6u&$Wp0VqR3*bDIWLE4Y64K ze08)CmeFrq2>QGFSDAk%Rhs}$r*rJVNuoO(~AJ!PG{T~d_i(dQ;OsQc+q&twwlJV|`Bv$N}R$K=uxCPyc!RBBXfRjRcZi5yAQk|YKj*>d`|Xw~ckP!!SW%^gsH z4oDR1AJt?S?}B;<&e0TPFsNAMQwxCt69o{uA>=K^qd1+MST3tptj8GHnN(upgb*ji zq`i%b+{{=o7ByB78@8!x_Gs&uqLOKv_6{gO2b4jbc8YT@EEzqBp!v_c?XXFx9Dq zb{!I|Nu<;4kZbyl3*LDg#$f7`nKwT9p9|2|t&fmAe64Of^c3TKI%Q?_^+uxaj|?xL zw5U4G#YlpQDngbfM)q85qt=DJt|y5nG){VqE;V8I&WBCAH+|pe@QT+};^BWB8(lGB zqe!DD7GqI`0pj%h;hm z;n?F&(5YS1X4{T?Hf24&;~ic?rDC*Zgk;*ga9b~Je`?R%gBQy3U5$!cEi-#s>T+d# zWH}Mbv|6p1R<`wiiPB32Gn*u}EQxC^LGJIR?H}~g*|#s5IQY`pJzcYP=0El5RWIen z8*k;5(^qldFJ}(enhxl1pnB_vPi5uu!@1|-9|Owd=%J>WPwQ>dkLW|!5WV<$<73Xb z{0CRJT1OpP567)vYea*J7*!3_M-nC`C)l*@dKzsw^5El5v)K$c-nf?sZ)?i>Gc=yt zg{xL=urnv{!j}h=hh{KFAjIS@=h9C!xJWW@nmR0Ns^Wrk)72_X;&VM@qLNZyn;-h1m-)j4PH{!#b7fObo=TF+Xw z)_t{JRqgNW{e9m)=MZ*rJl6A%IHK!gcqM)U)>TjF8ytMTRLpN39jns9J?@oOe47l4 z1dw7d06;*nuu_+V$6Qs4K>#PCRHVFExV^duw#+4>?(j) z*AHP%*L5@qEpM#j?*@5nOq@HlBR^5M@^_J9)U!&MV7N?QAAfFbdJaGWPgRws)6~+R z-NrZmx0V*7Od$!{dkY1w*wll3j_1b``)C%NHS6N>yBU998+?y%)4SU2YA} zA%$NKSGVi)4!sVH=l1lla~XcBLKrfnO2~CXCa>$GlX_p?dYsM`3%)hidhs()bzlDL zr7zEG>kK#SwpW`1YyR;!pa1&-`0t?)V)3FnK7V~pCo%hYIQUj+f?7Oh#@-(|a?XKA zr;?n->{Mx?{fOYn3n4;UD5a5kBx9Z>DQ1SETOzUjjZ`HF0&e`i-6T<17qM|ec7?fBc z;0k&%hz+o?+KMG>1)PSqUSqTR@!luCa_YiGo3TkPUp^w8T}r$YFf$gPyy|ZYU`={9 z3c4MNG|FgE6ETxVuw_~St-lefEMgF+NTdzZD8wWJ0s<69@frs3IxH*_A4`(dIZhJT z)TwApTxD36oOSS>-?;UKV^n{)k!mFpfWRL3*Rxl@V_bS?f`4@I!*C2lX%(H}L=`CT z0BxGtLQ@`yX#0U)3`bO@9NHBjM^*Gw64K=(1QdKEK*p+u<&qTSoUzKhfO`4Wz>@z)uK^Aw6m!k{QPq@f~bd?t)6?} z1bJ=k7!E&fDxUmP-(QVQ?F@i8a-dv4%Gg64haX`yNv^E%Ea<=YJ4SdqH4e{1~Sk?qbu|M;*f zbqpYh(szvQ9ev=Amrj8q0@9+|SbxTQw)=Lr&Hm@e_hY2mXXchai5dBmusvCYf%>!X zK>#8PKtTjx&+y*EIR|SkT*`=|2>VPq0kb=fM~F#u|GG<9sj?zc-#-8BqmC*-%N5t% z3v1um65bJjO9}`JV*qzjs9O-*vCma1qq%z0=Thg*sPtm8u4CiyU5H^JCTU0mH2?_M zGn{jci{Y)p`kvomV&MR6*th{{opqpyh3Ux4m)!GykUSWKMk@t>>SyNTwj2L%XZ{Nn z>Xv_j0zm+HA-wSFCJ4n;tqux{Z<*M!+ghP`mh}};q{({$d;y{&M#518E{~{H2e(KJ+~I! z(QA0${wLzt8F#!r1DoX%bYVIIT!6Y1 zJctN_2;>9AahjEz5Cm@p&;a2*ykj`$0UrSH$QJ^n3By@S!UCJh5jS2|HIuruyXF34 zRDv0v?9yEOYVFWR0jftU~yzAQIFKu_~N!vxLSpD zIxEmBpAwnRC3gEyg%Yon(xeEA2t*11fhfB~8i^HvMIcQOp5dF9V>l7DZ+tS31TC`?6B2!P-{Ai`NS%8sfWFCh_# z2!sJ<26G0;dxnUBNT3Wrj-j+52u(2zc*4ieoxAxfi_hFMD8$Dt*t4hHU+Z6a>y4`) z-dgRJ&wT2GICjQeJ24|X4P=?_kA+q7QY|L{F) z>E#!CslTU!sFuPzhBSJAZ4?NAGFdr600O~tQ;`JDd9Vkv#1X>KptUV8Q)hHgp)4=n zf7k1aF8a|v_e`5zKCDz~Nuz3ARYohScS~Kpws!0=fL0XBO0`T-YycqYn}yY@ZV?g2 zlnDnM86|@t(hM=mC6W&G)j}8N_Fwtr#>s`2R4qD9xuZ_o&BU=o5&`up5LX5DnnxN7 z(!|510_PdtJ9u$`Fq8(A0!#>KLogu_1c1^6@0sdRitRngzWe^er2PiAMIqpkE7Xj4 zqSD0i@PNn2cHaUJ;)tnGEM^?Y2OX%5fOPNhi#0IY;la!zy_Gm@B#Lw#(Mo_^%= znu44{7-|HeMy{k$Y%?&%Kq&>KG_*4CK85oRio&-@sE4y2Y3h;2*%j9ragC&24JaC` z`!uzlS%RjYWaMg=C2{s!Ax`QU03w3c0Yn(2{;azYNJdU3mn!CrxI&4*JCC^T#}y}2 zA`QzFa=EsmQ0RGvftbU zQ>{c90A|-98)Xj4nT0b0yyJf8t%xIraRd)QQ&z*I6o?d@PmrXe$eT_q-0f@}wCCAq zEl$Ss8*j&&jkjWZGSHg|Kx;aNPWFa9~0$jGSbWOU>XjH6xDc0w(iTEtcE6dO3#5TC{ScvW=I(b=Nv*)M5VtC-7j0@OiMO};u|K_aA+ua&Wy|G z0O?p6>sL7#>4bE^@$`cedW&;pHYGbq)cE=gVUygN~?!_hF|0teV`9}~ml+s!M!x_o7(s*;* zCVc-VU&If8em*{M)JJgGyiZ}QGSUDFC<*}~u!v@1)yzPXBMKoDa!^zNBmjHLN~pCo z86Fi-BjwE?n=_NmIA?K7liV3M;v_;xTNl23?ow=ga}EA*-%{NFA9)Ej6(HYiJs85m`CL9ANNz_7Wfw>}W{H&o zhy)^>0cdZXg2B-WvL1};5P}FJQvqpeDFK{}*W_F4Q?l}yJ$-+C<-Fxs|HfnZ?SC!9 z1CQT|j+S@fx%Cg={YRgO&z2Z>i~diz*O?*BnAkIbU{QcAP}Z33z=$xNR5+KgfMs35xDG&i*Vb0Kg44zZ^zZ& zc>uXE4-p1))`B-&1MC}R(r5-n0MAaC)!S!3D{E#4D+*c5&ME_7bO-`vnhuJ0%rG^y z*MSI{U{o_J!WqGvFVAW?BdzlmMhBQRZ2?B+Z$U21!?_gN1W=^F4PGQ^jHW1{`Cb9o zLx~8DXBkZ|AhymqMH-oHxQxU~>&7f9WD8o#QYOvxW(yKUdVH3~XXbxdwyFjxt+lAv zZaWSag=@ z=8P$&K}1lbY?iX@ee4?s0wKUBJ964=H$0STaA3T?n~R$9CTTo$W*+}*eEXdRL>ghx z0ulvhz0Z>9A)>e;5?WE{3wn~(Mxl@k5Z8vY60)g)Z7AM`NMj7L0~nqG?*MV$0cj#* zg?t%+Zb&IZs~iSLH{&P2T8vGbH$W*3fW~XQxiirODk4xy!&-;m-f<)T^zbbx6J$2bI!+g&Q(Tb>mTpfw(MhPbbX*24YD+xC~pjzlg4B?I0>ZG1eo;$GZ-@3q)Ayc(TT%9uB8CcO9K>t$rJ4+!Ga!{2blb3*{mJ?rAx;e_@g zW=}sb8SURhsg02gkr06Qo;))H{@ois2J0*E-a_ku;$#FwS}J2z^z{y5!Tf{u-m?$! zW7XmPw~xK}Y|U*DV-zVxM2Z?xn6(ROnxdy?JIXW%Qzy=WHv^~-wPRiPJ(xPPjP?m_ zU@!3AH)Mt2y@NuFGk%)cvT4gxH~;vV!~gKarE2vv&(f8P@Ag++xft8kE4o&xvN3^V zhgKTPzIFc&iMV*lvDmVC6ReMr3kzh>qKs;xT2uwI^KCQwiCuxGcI>;nX1mYH6|D_I zV?e$kJ`M5;L7M=zY84}cF$$#|Dx-Bwp4xT+U;&*D<@0j8tMo%x5%Tg?~5R?T=3cv%@lt|5rbf!U~$$KWHR3?Xk zu&I|c5%P}XIIb@4XrJ=aC`y!W*}^Y88R7A}hVa+MJ05U+?`P+M8rvjM6j3edroqA2 zxm4Kuj7oLnm$`fxbar$}K3^bGfWT*$Wd5R*hEfJ52%w-LATTp*YNZ}ksTNg7J=bnd z-Pkqa!RO=D(kYB&|Wjqg0rvF8kum{NfucTYqrP z`5U%u**G!G6{S=zQMp`3K3_yWUyzoz^2Q(tmC>3+s5Oq`4(BY=)S@2MFgiNo;u?&k zg`0}`37-~9P0%vHiA@+H2!cEy8o#>wuOImB)G_Pj7yce!TXGVt#ORn z(=jFB*q2Zp6$}lGp?}+$um^#4QjKaSEI75c$z6AAYL348>#uKEccl>fFbuUZ0R$d} zZ~}6sT!$|qC`YPurgrtQ76=RC$YS~T-}$t1r_YJ6x+vSq`|xwOl@gGLU>BhcFBv~FMie-ahi$Rz-LINpu0Hu~Za`}LYEdk2y0hQVU6k7}mB|~9e!x(}I6ii4k;VvE0 z?|KG+Oj%0Bi3m(dlp;$c5Cu`1CM@ypLV(%bX9 zr_WVSKiJ10x1!vdPr`gLXF?@f1r%~#N8UkH?XgO1p%e>?-DLnfb z=86?7j~f~sKElT8lSw^&-{|PJ_Z)D@o-cw6^yvN1aY@hS38meM!r|M7s_XW%93Aak za$IUh=gpcu=jzR`4$^18^F8_11#h4-#Jd^}{s&{CB`(>qac=+s03~!qSaf7zbY(hY za%Ew3WdJfTF)=MLIW00WR4_R@Gcr0eGA%GSIxsM(l48sN001R)MObuXVRU6WZEs|0 vW_bWIFflPLFgYzTHdHV-Ix;spGd3+SH##sdcWUue00000NkvXXu0mjfB?gph literal 0 HcmV?d00001 diff --git a/sample_itemlist/src/main/res/drawable-xhdpi/ic_launcher.png b/sample_itemlist/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..71c6d760f05183ef8a47c614d8d13380c8528499 GIT binary patch literal 14383 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 literal 0 HcmV?d00001 diff --git a/sample_itemlist/src/main/res/drawable-xxhdpi/ic_launcher.png b/sample_itemlist/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4df18946442ed763bd52cf3adca31617848656fa GIT binary patch literal 19388 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 literal 0 HcmV?d00001 diff --git a/sample_itemlist/src/main/res/drawable/drop_shadow.xml b/sample_itemlist/src/main/res/drawable/drop_shadow.xml new file mode 100644 index 0000000..2d50fed --- /dev/null +++ b/sample_itemlist/src/main/res/drawable/drop_shadow.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/sample_itemlist/src/main/res/layout/activity_main.xml b/sample_itemlist/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..0a6c112 --- /dev/null +++ b/sample_itemlist/src/main/res/layout/activity_main.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + +