├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── dictionaries │ └── maximefimov.xml ├── gradle.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── modules.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── redmadrobot │ │ └── chronos_sample │ │ ├── MainActivity.java │ │ ├── Sample.java │ │ ├── operations │ │ └── SimpleOperation.java │ │ └── samples │ │ ├── DataLoad.java │ │ ├── DataLoadCancel.java │ │ └── SimpleRun.java │ └── res │ ├── layout │ ├── activity_data_load.xml │ ├── activity_data_load_cancel.xml │ ├── activity_main.xml │ └── activity_simple_run.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ └── values │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── chronos ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── redmadrobot │ │ └── chronos │ │ ├── ActivityRunTest.java │ │ ├── BroadcastRunTest.java │ │ ├── FragmentRunTest.java │ │ ├── MemoryTest.java │ │ ├── SetupTest.java │ │ ├── TestSettings.java │ │ ├── mock │ │ ├── BigObject.java │ │ ├── gui │ │ │ ├── MockActivity.java │ │ │ ├── MockFragment.java │ │ │ ├── SampleHeavyActivity.java │ │ │ ├── SimpleMockActivity.java │ │ │ ├── SimpleMockFragment.java │ │ │ └── State.java │ │ └── operation │ │ │ ├── HeavyOperation.java │ │ │ ├── HeavyOperationResult.java │ │ │ ├── SimpleErrorOperation.java │ │ │ ├── SimpleOperation.java │ │ │ └── SimpleOperationResult.java │ │ └── util │ │ └── TimingUtils.java │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── redmadrobot │ └── chronos │ ├── Chronos.java │ ├── ChronosConnector.java │ ├── ChronosListener.java │ ├── ChronosListenerManager.java │ ├── ChronosOperation.java │ ├── ChronosOperationResult.java │ ├── ChronosService.java │ ├── RunningOperationStorage.java │ └── gui │ ├── ChronosConnectorWrapper.java │ ├── activity │ ├── ChronosActivity.java │ ├── ChronosAppCompatActivity.java │ └── ChronosSupportActivity.java │ └── fragment │ ├── ChronosFragment.java │ ├── ChronosSupportFragment.java │ └── dialog │ ├── ChronosDialogFragment.java │ └── ChronosSupportDialogFragment.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Built application files 4 | *.apk 5 | *.ap_ 6 | 7 | # Files for the Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | #Log Files 28 | *.log 29 | 30 | #Android Studio 31 | .idea 32 | *.iml -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Chronos -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/dictionaries/maximefimov.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 125 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Redmadrobot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chronos 2 | Android library that handles asynchronous jobs. 3 | To read the full description, please take a look at [wiki page](https://github.com/RedMadRobot/Chronos/wiki). 4 | 5 | To add Chronos to your project simply add the following code to `dependencies` section of your `build.gradle` file: 6 | 7 | `compile 'com.redmadrobot:chronos:1.0.7'` 8 | 9 | ## Release notes 10 | 11 | 1.0.7 12 | - Added AppCompat default Acitivty. Use this version only if your app uses compileSdkVersion >= 23. 13 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.redmadrobot.chronos_sample" 9 | minSdkVersion 9 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:23.1.1' 25 | compile project(':chronos') 26 | } 27 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/maximefimov/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/redmadrobot/chronos_sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.redmadrobot.chronos_sample; 2 | 3 | import com.redmadrobot.chronos_sample.samples.DataLoad; 4 | import com.redmadrobot.chronos_sample.samples.DataLoadCancel; 5 | import com.redmadrobot.chronos_sample.samples.SimpleRun; 6 | 7 | import android.app.Activity; 8 | import android.content.Intent; 9 | import android.os.Bundle; 10 | import android.support.annotation.NonNull; 11 | import android.view.View; 12 | import android.widget.AdapterView; 13 | import android.widget.ArrayAdapter; 14 | import android.widget.ListAdapter; 15 | import android.widget.ListView; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * Home screen for the sample app. 22 | * 23 | * @author maximefimov 24 | */ 25 | public final class MainActivity extends Activity { 26 | 27 | @Override 28 | protected void onCreate(final Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_main); 31 | 32 | final ListView sampleList = (ListView) findViewById(R.id.list_samples); 33 | 34 | final ListAdapter adapter = new ArrayAdapter<>(this, 35 | android.R.layout.simple_list_item_1, getSamples()); 36 | 37 | sampleList.setAdapter(adapter); 38 | sampleList.setOnItemClickListener(new AdapterView.OnItemClickListener() { 39 | @Override 40 | public void onItemClick(final AdapterView parent, final View view, 41 | final int position, final long id) { 42 | showSample((Sample) parent.getAdapter().getItem(position)); 43 | 44 | } 45 | }); 46 | } 47 | 48 | private List getSamples() { 49 | final List samples = new ArrayList<>(); 50 | 51 | samples.add(new Sample("Simple run", SimpleRun.class)); 52 | samples.add(new Sample("Data loading", DataLoad.class)); 53 | samples.add(new Sample("Data loading with cancel", DataLoadCancel.class)); 54 | 55 | return samples; 56 | } 57 | 58 | private void showSample(@NonNull final Sample sample) { 59 | startActivity(new Intent(this, sample.getActivityClass())); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/redmadrobot/chronos_sample/Sample.java: -------------------------------------------------------------------------------- 1 | package com.redmadrobot.chronos_sample; 2 | 3 | import android.app.Activity; 4 | import android.support.annotation.NonNull; 5 | 6 | /** 7 | * A entity represents a Chronos sample. 8 | * 9 | * @author maximefimov 10 | */ 11 | public final class Sample { 12 | 13 | private final String mName; 14 | 15 | private final Class mActivityClass; 16 | 17 | public Sample(@NonNull final String name, 18 | @NonNull final Class activityClass) { 19 | mName = name; 20 | mActivityClass = activityClass; 21 | } 22 | 23 | @NonNull 24 | public final Class getActivityClass() { 25 | return mActivityClass; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return mName; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/redmadrobot/chronos_sample/operations/SimpleOperation.java: -------------------------------------------------------------------------------- 1 | package com.redmadrobot.chronos_sample.operations; 2 | 3 | import com.redmadrobot.chronos.ChronosOperation; 4 | import com.redmadrobot.chronos.ChronosOperationResult; 5 | 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.Nullable; 8 | 9 | /** 10 | * An operation that calculates a string length, mocking it is being a time-consuming task. 11 | * 12 | * @author maximefimov 13 | */ 14 | public final class SimpleOperation extends ChronosOperation { 15 | 16 | private final String mInput; 17 | 18 | public SimpleOperation(@NonNull final String input) { 19 | mInput = input; 20 | } 21 | 22 | @Nullable 23 | @Override 24 | //Chronos will run this method in a background thread, which means you can put 25 | //any time-consuming calls here, as it will not affect UI thread performance 26 | public String run() { 27 | final String result = "String length is " + mInput.length(); 28 | 29 | try { 30 | Thread.sleep(3000); 31 | } catch (InterruptedException e) { 32 | // do nothing, thread is interrupted, which means a system wants to stop the run 33 | } 34 | 35 | return result; 36 | } 37 | 38 | @NonNull 39 | @Override 40 | // To be able to distinguish results from different operations in one Chronos client 41 | // (most commonly an activity, or a fragment) 42 | // you should create an 'OperationResult<>' subclass in each operation, 43 | // so that it will be used as a parameter 44 | // in a callback method 'onOperationFinished' 45 | public Class> getResultClass() { 46 | return Result.class; 47 | } 48 | 49 | public final static class Result extends ChronosOperationResult { 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/redmadrobot/chronos_sample/samples/DataLoad.java: -------------------------------------------------------------------------------- 1 | package com.redmadrobot.chronos_sample.samples; 2 | 3 | import com.redmadrobot.chronos.gui.activity.ChronosActivity; 4 | import com.redmadrobot.chronos_sample.R; 5 | import com.redmadrobot.chronos_sample.operations.SimpleOperation; 6 | 7 | import android.os.Bundle; 8 | import android.support.annotation.NonNull; 9 | import android.widget.TextView; 10 | 11 | /** 12 | * A sample of how you can easily initiate your layout with data provided by some source, that takes 13 | * some time to give a response, such as remote server, or a database. 14 | * 15 | * @author maximefimov 16 | */ 17 | public final class DataLoad extends ChronosActivity { 18 | 19 | // a key by which the activity saves and restores already loaded data 20 | private final static String KEY_DATA = "data"; 21 | 22 | // a tag which represents a group of operations that can't run simultaneously 23 | private final static String TAG_DATA_LOADING = "data_loading"; 24 | 25 | private TextView mTextOutput; 26 | 27 | // a data that has to be loaded 28 | private String mData = null; 29 | 30 | @Override 31 | protected void onCreate(final Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.activity_data_load); 34 | 35 | mTextOutput = (TextView) findViewById(R.id.text_output); 36 | 37 | if (savedInstanceState != null) { 38 | //first of all, the activity tries to restored already loaded data 39 | mData = savedInstanceState.getString(KEY_DATA); 40 | } 41 | } 42 | 43 | @Override 44 | protected void onResume() { 45 | super.onResume(); 46 | //after this point all pending OperationResults are delivered, so that you may be sure, 47 | // that all proper 'onOperationFinished' calls are done 48 | 49 | if (mData == null) {// if it is still no data 50 | // The activity launches a loading operations with a tag 51 | // so that if it comes to this point once again and the data is not loaded yet, 52 | // the next launch will be ignored. 53 | // That means, no matter now often user rotates the device, 54 | // only one operation with a given tag may be pending in a single moment of time. 55 | runOperation(new SimpleOperation(""), TAG_DATA_LOADING); 56 | } else { 57 | // If there is a data already, just show it; 58 | showData(); 59 | } 60 | } 61 | 62 | @Override 63 | protected void onSaveInstanceState(@NonNull final Bundle outState) { 64 | super.onSaveInstanceState(outState); 65 | // It's important to manually save loaded data, as for now Chronos doesn't have an built-in cache 66 | outState.putString(KEY_DATA, mData); 67 | } 68 | 69 | private void showData() { 70 | mTextOutput.setText("Data is '" + mData + "'"); 71 | } 72 | 73 | public void onOperationFinished(final SimpleOperation.Result result) { 74 | if (result.isSuccessful()) { 75 | // After the activity got the data, it is being saved to a local variable. 76 | // The programmer should take care of saving it during activity destroy-recreation process. 77 | mData = result.getOutput(); 78 | showData(); 79 | } else { 80 | // Here the negative result is not stored, so it 81 | mTextOutput.setText(result.getErrorMessage()); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/redmadrobot/chronos_sample/samples/DataLoadCancel.java: -------------------------------------------------------------------------------- 1 | package com.redmadrobot.chronos_sample.samples; 2 | 3 | import com.redmadrobot.chronos.gui.activity.ChronosActivity; 4 | import com.redmadrobot.chronos_sample.R; 5 | import com.redmadrobot.chronos_sample.operations.SimpleOperation; 6 | 7 | import android.os.Bundle; 8 | import android.support.annotation.NonNull; 9 | import android.view.View; 10 | import android.widget.TextView; 11 | import android.widget.Toast; 12 | 13 | /** 14 | * An sample of it is possible to cancell a running operation. 15 | * 16 | * @author maximefimov 17 | */ 18 | public final class DataLoadCancel extends ChronosActivity { 19 | 20 | private final static String KEY_DATA = "data"; 21 | 22 | private final static String TAG_DATA_LOADING = "data_loading"; 23 | 24 | private TextView mTextOutput; 25 | 26 | private String mData = null; 27 | 28 | @Override 29 | protected void onCreate(final Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_data_load_cancel); 32 | 33 | mTextOutput = (TextView) findViewById(R.id.text_output); 34 | 35 | if (savedInstanceState != null) { 36 | mData = savedInstanceState.getString(KEY_DATA); 37 | } 38 | 39 | final View cancelView = findViewById(R.id.button_cancel); 40 | cancelView.setOnClickListener(new View.OnClickListener() { 41 | @Override 42 | public void onClick(final View v) { 43 | // in any moment of time you can try to cancel a running operation 44 | // though the run may not be cancelled, e.g. the run has been finished already, or never been issued at all 45 | final boolean cancelResult = cancelOperation(TAG_DATA_LOADING); 46 | if (cancelResult) { 47 | mTextOutput.setText("Operation launch is cancelled"); 48 | } else { 49 | showToast("Can't cancel operation launch"); 50 | } 51 | } 52 | }); 53 | } 54 | 55 | @Override 56 | protected void onResume() { 57 | super.onResume(); 58 | if (mData == null) { 59 | runOperation(new SimpleOperation(""), TAG_DATA_LOADING); 60 | } else { 61 | showData(); 62 | } 63 | } 64 | 65 | @Override 66 | protected void onSaveInstanceState(@NonNull final Bundle outState) { 67 | super.onSaveInstanceState(outState); 68 | outState.putString(KEY_DATA, mData); 69 | } 70 | 71 | private void showData() { 72 | mTextOutput.setText("Data is '" + mData + "'"); 73 | } 74 | 75 | // if Operations run is cancelled, there would be no call to this method, thus you should handle 76 | // cancelling in the place it happened 77 | public void onOperationFinished(final SimpleOperation.Result result) { 78 | if (result.isSuccessful()) { 79 | mData = result.getOutput(); 80 | showData(); 81 | } else { 82 | mTextOutput.setText(result.getErrorMessage()); 83 | } 84 | } 85 | 86 | private void showToast(@NonNull final String message) { 87 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/redmadrobot/chronos_sample/samples/SimpleRun.java: -------------------------------------------------------------------------------- 1 | package com.redmadrobot.chronos_sample.samples; 2 | 3 | import com.redmadrobot.chronos.gui.activity.ChronosActivity; 4 | import com.redmadrobot.chronos_sample.R; 5 | import com.redmadrobot.chronos_sample.operations.SimpleOperation; 6 | 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.widget.EditText; 10 | import android.widget.TextView; 11 | 12 | /** 13 | * A sample of how to use Chronos in a most minimalistic way. 14 | * 15 | * @author maximefimov 16 | */ 17 | public final class SimpleRun extends ChronosActivity { 18 | 19 | private TextView mTextOutput; 20 | 21 | @Override 22 | protected void onCreate(final Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_simple_run); 25 | 26 | final EditText editInput = (EditText) findViewById(R.id.edit_input); 27 | final View startView = findViewById(R.id.button_start); 28 | mTextOutput = (TextView) findViewById(R.id.text_output); 29 | 30 | startView.setOnClickListener(new View.OnClickListener() { 31 | @Override 32 | public void onClick(final View v) { 33 | // call the 'runOperation' method and it will begin executing of the operation 34 | // in background thread, so it will not block your GUI. 35 | runOperation(new SimpleOperation(editInput.getText().toString())); 36 | // after run is started, you can rotate, or put the sample to background 37 | // but the result of the operation will be delivered to an 'onOperationFinished' method 38 | // when the app goes to foreground once again. 39 | } 40 | }); 41 | } 42 | 43 | //most IDEs may assume this method as 'not-used', which is a side effect of Chronos software design, 44 | //so may want to suppress the warning to not get confused 45 | public void onOperationFinished(final SimpleOperation.Result result) { 46 | //Here you process the result 47 | if (result 48 | .isSuccessful()) { // this case happens when no exception was thrown during the operation run 49 | mTextOutput.setText(result.getOutput()); 50 | } else { // this happens if there was an exception 51 | mTextOutput.setText(result.getErrorMessage()); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_data_load.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_data_load_cancel.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 12 | 13 |