├── .gitignore ├── LICENSE ├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── data.sql │ ├── devices.db.1.sql │ ├── devices.db.2.sql │ ├── devices.db.3.sql │ └── response.json │ ├── java │ └── me │ │ └── adamstroud │ │ └── devicedatabase │ │ ├── DeviceDatabaseApplication.java │ │ ├── api │ │ ├── retrofit │ │ │ ├── DeviceService.java │ │ │ ├── ManufacturersAndDevicesResponse.java │ │ │ └── WebServiceClient.java │ │ └── volley │ │ │ ├── GetManufacturersAndDevicesRequest.java │ │ │ ├── JacksonRequest.java │ │ │ └── VolleyApiClient.java │ │ ├── device │ │ ├── AddDeviceActivity.java │ │ ├── DeviceDetailActivity.java │ │ ├── DeviceListActivity.java │ │ └── NetworkCallAsyncTask.java │ │ ├── manufacturer │ │ ├── AddManufacturerActivity.java │ │ └── ManufacturerListActivity.java │ │ ├── model │ │ ├── Device.java │ │ └── Manufacturer.java │ │ ├── provider │ │ ├── DevicesContract.java │ │ ├── DevicesOpenHelper.java │ │ └── DevicesProvider.java │ │ └── sync │ │ ├── Authenticator.java │ │ ├── AuthenticatorService.java │ │ ├── SyncAdapter.java │ │ ├── SyncManager.java │ │ └── SyncService.java │ └── res │ ├── drawable-hdpi │ ├── ic_add_white_24dp.png │ ├── ic_done_white_24dp.png │ ├── ic_search_white_24dp.png │ └── ic_share_white_24dp.png │ ├── drawable-mdpi │ ├── ic_add_white_24dp.png │ ├── ic_done_white_24dp.png │ ├── ic_search_white_24dp.png │ └── ic_share_white_24dp.png │ ├── drawable-xhdpi │ ├── ic_add_white_24dp.png │ ├── ic_done_white_24dp.png │ ├── ic_search_white_24dp.png │ └── ic_share_white_24dp.png │ ├── drawable-xxhdpi │ ├── ic_add_white_24dp.png │ ├── ic_done_white_24dp.png │ ├── ic_search_white_24dp.png │ └── ic_share_white_24dp.png │ ├── drawable-xxxhdpi │ ├── ic_add_white_24dp.png │ ├── ic_done_white_24dp.png │ ├── ic_search_white_24dp.png │ └── ic_share_white_24dp.png │ ├── layout │ ├── activity_add_device.xml │ ├── activity_add_manufacturer.xml │ ├── activity_device_detail.xml │ ├── activity_device_list.xml │ ├── activity_manufacturer_list.xml │ ├── appbar.xml │ ├── list_item_device.xml │ └── list_item_manufacturer.xml │ ├── menu │ ├── activity_add_device.xml │ ├── activity_add_manufacturer.xml │ ├── activity_device_detail.xml │ └── activity_device_list.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-v21 │ └── styles.xml │ ├── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ ├── authenticator.xml │ └── syncadapter.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── thirdParty └── volley ├── .gitignore ├── Android.mk ├── README ├── build.gradle ├── build.xml ├── custom_rules.xml ├── pom.xml ├── proguard-project.txt ├── proguard.cfg ├── rules.gradle └── src ├── main ├── AndroidManifest.xml └── java │ └── com │ └── android │ └── volley │ ├── AuthFailureError.java │ ├── Cache.java │ ├── CacheDispatcher.java │ ├── DefaultRetryPolicy.java │ ├── ExecutorDelivery.java │ ├── Network.java │ ├── NetworkDispatcher.java │ ├── NetworkError.java │ ├── NetworkResponse.java │ ├── NoConnectionError.java │ ├── ParseError.java │ ├── Request.java │ ├── RequestQueue.java │ ├── Response.java │ ├── ResponseDelivery.java │ ├── RetryPolicy.java │ ├── ServerError.java │ ├── TimeoutError.java │ ├── VolleyError.java │ ├── VolleyLog.java │ └── toolbox │ ├── AndroidAuthenticator.java │ ├── Authenticator.java │ ├── BasicNetwork.java │ ├── ByteArrayPool.java │ ├── ClearCacheRequest.java │ ├── DiskBasedCache.java │ ├── HttpClientStack.java │ ├── HttpHeaderParser.java │ ├── HttpStack.java │ ├── HurlStack.java │ ├── ImageLoader.java │ ├── ImageRequest.java │ ├── JsonArrayRequest.java │ ├── JsonObjectRequest.java │ ├── JsonRequest.java │ ├── NetworkImageView.java │ ├── NoCache.java │ ├── PoolingByteArrayOutputStream.java │ ├── RequestFuture.java │ ├── StringRequest.java │ └── Volley.java └── test ├── java └── com │ └── android │ └── volley │ ├── CacheDispatcherTest.java │ ├── NetworkDispatcherTest.java │ ├── RequestQueueIntegrationTest.java │ ├── RequestQueueTest.java │ ├── RequestTest.java │ ├── ResponseDeliveryTest.java │ ├── mock │ ├── MockCache.java │ ├── MockHttpClient.java │ ├── MockHttpStack.java │ ├── MockHttpURLConnection.java │ ├── MockNetwork.java │ ├── MockRequest.java │ ├── MockResponseDelivery.java │ ├── ShadowSystemClock.java │ ├── TestRequest.java │ └── WaitableQueue.java │ ├── toolbox │ ├── AndroidAuthenticatorTest.java │ ├── BasicNetworkTest.java │ ├── ByteArrayPoolTest.java │ ├── CacheTest.java │ ├── DiskBasedCacheTest.java │ ├── HttpClientStackTest.java │ ├── HttpHeaderParserTest.java │ ├── HurlStackTest.java │ ├── ImageLoaderTest.java │ ├── ImageRequestTest.java │ ├── JsonRequestCharsetTest.java │ ├── JsonRequestTest.java │ ├── NetworkImageViewTest.java │ ├── PoolingByteArrayOutputStreamTest.java │ ├── RequestFutureTest.java │ ├── RequestQueueTest.java │ ├── RequestTest.java │ ├── ResponseTest.java │ └── StringRequestTest.java │ └── utils │ ├── CacheTestUtils.java │ └── ImmediateResponseDelivery.java └── resources └── org.robolectric.Config.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | /*/build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # IntelliJ/Android Studio project files 30 | .idea/ 31 | *.iml 32 | *.ipr 33 | *.iws 34 | captures/ 35 | 36 | # Eclipse project files 37 | .settings/ 38 | .project 39 | .classpath 40 | 41 | # Mac OS files 42 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android™ Database Best Practices 2 | 3 | This repository contains the source code that was discussed in 4 | [Android™ Database Best Practices](https://www.pearsonhighered.com/program/Stroud-Android-Database-Best-Practices/PGM332735.html). 5 | The code resides in a [Gradle](http://www.gradle.org) project that can be imported into 6 | [Android™ Studio](https://developer.android.com/studio/index.html) and run on a device. 7 | 8 | ## Description 9 | 10 | **Battle-Tested Strategies for Storing, Managing, and Sharing Android Data** 11 | 12 | This is the first guide to focus on one of the most critical aspects of Android development: how to 13 | efficiently store, retrieve, manage, and share information from your app’s internal database. 14 | Through real-world code examples, which you can use in your own apps, you’ll learn how to take full 15 | advantage of Android’s SQLite database and related classes. 16 | 17 | The newest title in Addison-Wesley’s [Android™ Deep Dive](https://www.pearsonhighered.com/series/Android-Deep-Dive/4146853.html) 18 | series for experienced Android developers, [Android™ Database Best Practices](https://www.pearsonhighered.com/program/Stroud-Android-Database-Best-Practices/PGM332735.html) 19 | draws on Adam Stroud’s extensive experience leading cutting-edge app projects. 20 | 21 | First, Stroud reviews the core database theory and SQL techniques you need to efficiently build, 22 | manipulate, and read SQLite databases. Next, he explores SQLite in detail, illuminates Android’s 23 | APIs for database interaction, and shares modern best practices for working with databases in the 24 | Android environment. 25 | 26 | Through a complete case study, you’ll learn how to design your data access layer to simplify all 27 | facets of data management and avoid unwanted technical debt. You’ll also find detailed solutions for 28 | common challenges in building data-enabled Android apps, including issues associated with threading, 29 | remote data access, and showing data to users. 30 | 31 | With this indispensable guide, you will: 32 | + Discover how Android’s lightweight SQLite database differs from other relational databases 33 | + Use SQL DDL to add structure to a database, and use DML to manipulate data 34 | + Define and work with SQLite data types 35 | + Persist highly structured data for fast, efficient access 36 | + Master Android classes for create, read, update, and delete (CRUD) operations and database queries 37 | + Share data within or between apps via content providers 38 | + Master efficient UI strategies for displaying data, while accounting for threading issues 39 | + Use Android’s Intents API to pass data between activities when starting a new activity or service 40 | + Achieve two-way communication between apps and remote web APIs 41 | + Manage the complexities of app-to-server communication, and avoid common problems 42 | + Use Android’s new Data Binding API to write less code and improve performance 43 | 44 | 45 | ## Contact 46 | + [Email](mailto:adam.stroud@gmail.com) 47 | 48 | ## License 49 | 50 | All the code is licensed under the Apache 2.0 license. See [LICENSE.txt](https://github.com/android-database-best-practices/device-database/blob/master/LICENSE) for license details. -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | apply plugin: 'com.android.application' 18 | 19 | android { 20 | compileSdkVersion rootProject.ext.compileSdkVersion 21 | buildToolsVersion rootProject.ext.buildToolsVersion 22 | 23 | defaultConfig { 24 | applicationId "me.adamstroud.devicedatabase" 25 | minSdkVersion 15 26 | targetSdkVersion 24 27 | versionCode 1 28 | versionName "1.1-SNAPSHOT" 29 | } 30 | buildTypes { 31 | release { 32 | minifyEnabled false 33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 34 | } 35 | } 36 | packagingOptions { 37 | exclude 'META-INF/NOTICE' 38 | exclude 'META-INF/LICENSE' 39 | } 40 | 41 | dataBinding { 42 | enabled = true 43 | } 44 | } 45 | 46 | dependencies { 47 | compile fileTree(dir: 'libs', include: ['*.jar']) 48 | compile project(':thirdParty:volley') 49 | 50 | testCompile 'junit:junit:4.12' 51 | 52 | compile rootProject.ext.supportLibraryDependencies.appCompat 53 | compile rootProject.ext.supportLibraryDependencies.design 54 | compile rootProject.ext.supportLibraryDependencies.recyclerView 55 | compile rootProject.ext.supportLibraryDependencies.cardView 56 | 57 | final RETROFIT_VERSION = '2.0.0' 58 | compile "com.squareup.retrofit2:retrofit:${RETROFIT_VERSION}" 59 | compile "com.squareup.retrofit2:converter-gson:${RETROFIT_VERSION}" 60 | compile "com.squareup.okhttp3:logging-interceptor:3.2.0" 61 | compile 'com.fasterxml.jackson.core:jackson-databind:2.7.0' 62 | compile "com.squareup.retrofit2:adapter-rxjava:${RETROFIT_VERSION}" 63 | 64 | compile 'io.reactivex:rxandroid:1.1.0' 65 | // Because RxAndroid releases are few and far between, it is 66 | // recommended you also explicitly depend on RxJava's latest version 67 | // for bug fixes and new features. 68 | compile 'io.reactivex:rxjava:1.1.3' 69 | 70 | compile 'com.facebook.stetho:stetho:1.3.1' 71 | compile 'com.facebook.stetho:stetho-okhttp3:1.3.1' 72 | } 73 | -------------------------------------------------------------------------------- /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/adstro/Applications/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # ################################################################################################## 20 | # Retrofit2 (https://square.github.io/retrofit/) 21 | # ################################################################################################## 22 | -dontwarn retrofit2.** 23 | -keep class retrofit2.** { *; } 24 | -keepattributes Signature 25 | -keepattributes Exceptions -------------------------------------------------------------------------------- /app/src/main/assets/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO manufacturer (short_name, long_name) 2 | VALUES ('HTC', 'HTC Corporation'); 3 | 4 | INSERT INTO manufacturer (short_name, long_name) 5 | VALUES ('Samsung', 'Samsung Electronics'); 6 | 7 | INSERT INTO manufacturer (short_name, long_name) 8 | VALUES ('LG', 'LG Electronics'); 9 | 10 | INSERT INTO device (model, nickname, display_size_inches, manufacturer_id) 11 | VALUES ('Nexus One', 'Passion', 3.7, (SELECT _id 12 | FROM manufacturer 13 | WHERE short_name = 'HTC')); 14 | 15 | INSERT INTO device (model, nickname, display_size_inches, manufacturer_id) 16 | VALUES ('Nexus S', 'Crespo', 4.0, (SELECT _id 17 | FROM manufacturer 18 | WHERE short_name = 'Samsung')); 19 | 20 | INSERT INTO device (model, nickname, display_size_inches, manufacturer_id) 21 | VALUES ('Galaxy Nexus', 'Toro', 4.65, (SELECT _id 22 | FROM manufacturer 23 | WHERE short_name = 'Samsung')); 24 | 25 | INSERT INTO device (model, nickname, display_size_inches, manufacturer_id) 26 | VALUES ('Nexus 4', 'Mako', 4.7, (SELECT _id 27 | FROM manufacturer 28 | WHERE short_name = 'LG')); -------------------------------------------------------------------------------- /app/src/main/assets/devices.db.1.sql: -------------------------------------------------------------------------------- 1 | -- device table 2 | CREATE TABLE device (_id INTEGER PRIMARY KEY AUTOINCREMENT, 3 | model TEXT NOT NULL, 4 | nickname TEXT, 5 | memory_mb REAL, 6 | display_size_inches REAL, 7 | manufacturer_id INTEGER REFERENCES manufacturer(_id) ON DELETE CASCADE); 8 | CREATE INDEX idx_device_model ON device(model); 9 | 10 | -- manufacturer table 11 | CREATE TABLE manufacturer(_id INTEGER PRIMARY KEY AUTOINCREMENT, 12 | short_name TEXT, 13 | long_name); -------------------------------------------------------------------------------- /app/src/main/assets/devices.db.2.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE table_2 (_id INTEGER PRIMARY KEY AUTOINCREMENT); -------------------------------------------------------------------------------- /app/src/main/assets/devices.db.3.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE table_3 (_id INTEGER PRIMARY KEY AUTOINCREMENT); -------------------------------------------------------------------------------- /app/src/main/assets/response.json: -------------------------------------------------------------------------------- 1 | { 2 | "manufacturers": [ 3 | { 4 | "short_name": "Samsung", 5 | "long_name": "Samsung Electronics", 6 | "devices": [ 7 | { 8 | "model": "Nexus S", 9 | "nickname": "Crespo", 10 | "display_size_inches": 4.0 11 | }, 12 | { 13 | "model": "Galaxy Nexus", 14 | "nickname": "Toro", 15 | "display_size_inches": 4.65 16 | } 17 | ] 18 | }, 19 | { 20 | "short_name": "LG", 21 | "long_name": "LG Electronics", 22 | "devices": [ 23 | { 24 | "model": "Nexus 4", 25 | "nickname": "Mako", 26 | "display_size_inches": 4.7 27 | } 28 | ] 29 | }, 30 | { 31 | "short_name": "HTC", 32 | "long_name": "HTC Corporation", 33 | "devices": [ 34 | { 35 | "model": "Nexus One", 36 | "nickname": "Passion", 37 | "display_size_inches": 3.7 38 | } 39 | ] 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/DeviceDatabaseApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase; 18 | 19 | import android.app.Application; 20 | 21 | import com.facebook.stetho.Stetho; 22 | 23 | /** 24 | * The application for the class 25 | * 26 | * @author Adam Stroud <adam.stroud@gmail.com> 27 | */ 28 | public class DeviceDatabaseApplication extends Application { 29 | @Override 30 | public void onCreate() { 31 | super.onCreate(); 32 | 33 | if (BuildConfig.DEBUG) { 34 | Stetho.initializeWithDefaults(this); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/api/retrofit/DeviceService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.api.retrofit; 18 | 19 | import retrofit2.Call; 20 | import retrofit2.http.GET; 21 | import rx.Observable; 22 | 23 | /** 24 | * The interface for the web API. 25 | * 26 | * @author Adam Stroud <adam.stroud@gmail.com> 27 | */ 28 | public interface DeviceService { 29 | @GET("v2/570bbaf6110000b003d17e3a") 30 | Call getManufacturersAndDevices(); 31 | 32 | @GET("v2/570bbaf6110000b003d17e3a") 33 | Observable rxGetManufacturersAndDevices(); 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/api/retrofit/ManufacturersAndDevicesResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.api.retrofit; 18 | 19 | import java.util.List; 20 | 21 | import me.adamstroud.devicedatabase.model.Manufacturer; 22 | 23 | /** 24 | * The response to the {@link me.adamstroud.devicedatabase.api.volley.GetManufacturersAndDevicesRequest} request. 25 | * 26 | * @author Adam Stroud <adam.stroud@gmail.com> 27 | */ 28 | public class ManufacturersAndDevicesResponse { 29 | private List manufacturers; 30 | 31 | public List getManufacturers() { 32 | return manufacturers; 33 | } 34 | 35 | public void setManufacturers(List manufacturers) { 36 | this.manufacturers = manufacturers; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/api/retrofit/WebServiceClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.api.retrofit; 18 | 19 | import android.util.Log; 20 | 21 | import com.google.gson.FieldNamingPolicy; 22 | import com.google.gson.Gson; 23 | import com.google.gson.GsonBuilder; 24 | 25 | import me.adamstroud.devicedatabase.BuildConfig; 26 | import okhttp3.OkHttpClient; 27 | import okhttp3.logging.HttpLoggingInterceptor; 28 | import retrofit2.Retrofit; 29 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 30 | import retrofit2.converter.gson.GsonConverterFactory; 31 | 32 | /** 33 | * The singleton for the web API client. 34 | * 35 | * @author Adam Stroud <adam.stroud@gmail.com> 36 | */ 37 | public class WebServiceClient { 38 | private static final String TAG = 39 | WebServiceClient.class.getSimpleName(); 40 | 41 | private static WebServiceClient instance = new WebServiceClient(); 42 | 43 | private final DeviceService service; 44 | 45 | public static WebServiceClient getInstance() { 46 | return instance; 47 | } 48 | 49 | private WebServiceClient() { 50 | final Gson gson = new GsonBuilder() 51 | .setFieldNamingPolicy(FieldNamingPolicy 52 | .LOWER_CASE_WITH_UNDERSCORES) 53 | .create(); 54 | 55 | Retrofit.Builder retrofitBuilder = new Retrofit.Builder() 56 | .baseUrl("http://www.mocky.io") 57 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 58 | .addConverterFactory(GsonConverterFactory.create(gson)); 59 | 60 | if (BuildConfig.DEBUG) { 61 | final HttpLoggingInterceptor loggingInterceptor = 62 | new HttpLoggingInterceptor(new HttpLoggingInterceptor 63 | .Logger() { 64 | @Override 65 | public void log(String message) { 66 | Log.d(TAG, message); 67 | } 68 | }); 69 | 70 | retrofitBuilder.callFactory(new OkHttpClient 71 | .Builder() 72 | .addNetworkInterceptor(loggingInterceptor) 73 | .build()); 74 | 75 | loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 76 | } 77 | 78 | service = retrofitBuilder.build().create(DeviceService.class); 79 | } 80 | 81 | public DeviceService getService() { 82 | return service; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/api/volley/GetManufacturersAndDevicesRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.api.volley; 18 | 19 | import com.android.volley.Response.ErrorListener; 20 | import com.android.volley.Response.Listener; 21 | 22 | import java.util.List; 23 | 24 | import me.adamstroud.devicedatabase.model.Manufacturer; 25 | 26 | /** 27 | * Returns the manufacturer/device list. 28 | * 29 | * @author Adam Stroud <adam.stroud@gmail.com> 30 | */ 31 | public class GetManufacturersAndDevicesRequest 32 | extends JacksonRequest { 33 | public GetManufacturersAndDevicesRequest(Object tag, 34 | Listener listener, 35 | ErrorListener errorListener) { 36 | super(Method.GET, 37 | "http://www.mocky.io/v2/570bbaf6110000b003d17e3a", 38 | Response.class, 39 | listener, 40 | errorListener); 41 | 42 | this.setTag(tag); 43 | } 44 | 45 | public static class Response { 46 | private List manufacturers; 47 | 48 | public List getManufacturers() { 49 | return manufacturers; 50 | } 51 | 52 | public void setManufacturers(List manufacturers) { 53 | this.manufacturers = manufacturers; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/api/volley/JacksonRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.api.volley; 18 | 19 | import com.android.volley.NetworkResponse; 20 | import com.android.volley.ParseError; 21 | import com.android.volley.Request; 22 | import com.android.volley.Response; 23 | import com.android.volley.toolbox.HttpHeaderParser; 24 | import com.fasterxml.jackson.annotation.JsonInclude; 25 | import com.fasterxml.jackson.databind.ObjectMapper; 26 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 27 | 28 | import java.io.IOException; 29 | 30 | /** 31 | * Processes a JSON request using the Jackson JSON parser. 32 | * 33 | * @author Adam Stroud <adam.stroud@gmail.com> 34 | */ 35 | public class JacksonRequest extends Request { 36 | private static final ObjectMapper objectMapper = new ObjectMapper() 37 | .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) 38 | .setSerializationInclusion(JsonInclude.Include.NON_NULL); 39 | 40 | private final Response.Listener listener; 41 | private final Class clazz; 42 | 43 | public JacksonRequest(int method, 44 | String url, 45 | Class clazz, 46 | Response.Listener listener, 47 | Response.ErrorListener errorListener) { 48 | super(method, url, errorListener); 49 | 50 | this.listener = listener; 51 | this.clazz = clazz; 52 | } 53 | 54 | @Override 55 | protected Response parseNetworkResponse(NetworkResponse response) { 56 | T responsePayload; 57 | 58 | try { 59 | responsePayload = objectMapper.readValue(response.data, 60 | clazz); 61 | 62 | return Response.success(responsePayload, 63 | HttpHeaderParser.parseCacheHeaders(response)); 64 | } catch (IOException e) { 65 | return Response.error(new ParseError(e)); 66 | } 67 | } 68 | 69 | @Override 70 | protected void deliverResponse(T response) { 71 | listener.onResponse(response); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/api/volley/VolleyApiClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.api.volley; 18 | 19 | import android.content.Context; 20 | 21 | import com.android.volley.Request; 22 | import com.android.volley.RequestQueue; 23 | import com.android.volley.toolbox.Volley; 24 | 25 | /** 26 | * The Volley web API client. 27 | * 28 | * @author Adam Stroud <adam.stroud@gmail.com> 29 | */ 30 | public class VolleyApiClient { 31 | private static VolleyApiClient instance; 32 | 33 | private RequestQueue requestQueue; 34 | 35 | public static synchronized VolleyApiClient getInstance(Context ctx) { 36 | if (instance == null) { 37 | instance = new VolleyApiClient(ctx); 38 | } 39 | 40 | return instance; 41 | } 42 | 43 | private VolleyApiClient(Context context) { 44 | requestQueue = 45 | Volley.newRequestQueue(context.getApplicationContext()); 46 | } 47 | 48 | public Request add(Request request) { 49 | return requestQueue.add(request); 50 | } 51 | 52 | public void cancelAll(Object tag) { 53 | requestQueue.cancelAll(tag); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/manufacturer/AddManufacturerActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.manufacturer; 18 | 19 | import android.content.AsyncQueryHandler; 20 | import android.content.ContentUris; 21 | import android.content.ContentValues; 22 | import android.net.Uri; 23 | import android.os.Bundle; 24 | import android.support.design.widget.CoordinatorLayout; 25 | import android.support.design.widget.FloatingActionButton; 26 | import android.support.design.widget.Snackbar; 27 | import android.support.design.widget.TextInputLayout; 28 | import android.support.v7.app.AppCompatActivity; 29 | import android.support.v7.widget.Toolbar; 30 | import android.view.MenuItem; 31 | import android.view.View; 32 | 33 | import me.adamstroud.devicedatabase.R; 34 | import me.adamstroud.devicedatabase.provider.DevicesContract; 35 | 36 | public class AddManufacturerActivity extends AppCompatActivity { 37 | private CoordinatorLayout root; 38 | private TextInputLayout shortNameView; 39 | private TextInputLayout longNameView; 40 | private AddManufacturerAsyncQueryHandler queryHandler; 41 | 42 | @Override 43 | protected void onCreate(Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | setContentView(R.layout.activity_add_manufacturer); 46 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 47 | setSupportActionBar(toolbar); 48 | 49 | root = (CoordinatorLayout) findViewById(R.id.root); 50 | shortNameView = (TextInputLayout) findViewById(R.id.short_name); 51 | longNameView = (TextInputLayout) findViewById(R.id.long_name); 52 | queryHandler = new AddManufacturerAsyncQueryHandler(); 53 | 54 | FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); 55 | fab.setOnClickListener(new View.OnClickListener() { 56 | @Override 57 | public void onClick(View view) { 58 | Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) 59 | .setAction("Action", null).show(); 60 | } 61 | }); 62 | 63 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 64 | } 65 | 66 | public void onActionDoneClick(MenuItem menuItem) { 67 | final ContentValues contentValues = new ContentValues(); 68 | contentValues.put(DevicesContract.Manufacturer.SHORT_NAME, shortNameView.getEditText().getText().toString()); 69 | contentValues.put(DevicesContract.Manufacturer.LONG_NAME, longNameView.getEditText().getText().toString()); 70 | 71 | queryHandler.startInsert(1, null, DevicesContract.Manufacturer.CONTENT_URI, contentValues); 72 | } 73 | 74 | private class AddManufacturerAsyncQueryHandler extends AsyncQueryHandler { 75 | public AddManufacturerAsyncQueryHandler() { 76 | super(getContentResolver()); 77 | } 78 | 79 | @Override 80 | protected void onInsertComplete(int token, Object cookie, Uri uri) { 81 | super.onInsertComplete(token, cookie, uri); 82 | Snackbar.make(root, getString(R.string.device_saved_message, ContentUris.parseId(uri)), Snackbar.LENGTH_SHORT).show(); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/model/Device.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.model; 18 | 19 | /** 20 | * A device. 21 | * 22 | * @author Adam Stroud <adam.stroud@gmail.com> 23 | */ 24 | public class Device { 25 | private String model; 26 | private String nickname; 27 | private float memoryMb; 28 | private float displaySizeInches; 29 | 30 | public String getModel() { 31 | return model; 32 | } 33 | 34 | public void setModel(String model) { 35 | this.model = model; 36 | } 37 | 38 | public String getNickname() { 39 | return nickname; 40 | } 41 | 42 | public void setNickname(String nickname) { 43 | this.nickname = nickname; 44 | } 45 | 46 | public float getMemoryMb() { 47 | return memoryMb; 48 | } 49 | 50 | public void setMemoryMb(float memoryMb) { 51 | this.memoryMb = memoryMb; 52 | } 53 | 54 | public float getDisplaySizeInches() { 55 | return displaySizeInches; 56 | } 57 | 58 | public void setDisplaySizeInches(float displaySizeInches) { 59 | this.displaySizeInches = displaySizeInches; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/model/Manufacturer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.model; 18 | 19 | import java.util.List; 20 | 21 | /** 22 | * A Device manufacturer. 23 | * 24 | * @author Adam Stroud <adam.stroud@gmail.com> 25 | */ 26 | public class Manufacturer { 27 | private String shortName; 28 | private String longName; 29 | private List devices; 30 | 31 | public String getShortName() { 32 | return shortName; 33 | } 34 | 35 | public void setShortName(String shortName) { 36 | this.shortName = shortName; 37 | } 38 | 39 | public String getLongName() { 40 | return longName; 41 | } 42 | 43 | public void setLongName(String longName) { 44 | this.longName = longName; 45 | } 46 | 47 | public List getDevices() { 48 | return devices; 49 | } 50 | 51 | public void setDevices(List devices) { 52 | this.devices = devices; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/provider/DevicesContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.provider; 18 | 19 | import android.content.ContentResolver; 20 | import android.net.Uri; 21 | import android.provider.BaseColumns; 22 | 23 | import me.adamstroud.devicedatabase.BuildConfig; 24 | 25 | /** 26 | * Defines the API for {@link DevicesProvider}. 27 | * 28 | * @author Adam Stroud <adam.stroud@gmail.com> 29 | */ 30 | public final class DevicesContract { 31 | public static final String AUTHORITY = 32 | String.format("%s.provider", BuildConfig.APPLICATION_ID); 33 | 34 | public static final Uri AUTHORITY_URI = new Uri.Builder() 35 | .scheme(ContentResolver.SCHEME_CONTENT) 36 | .authority(AUTHORITY) 37 | .build(); 38 | 39 | public interface Device extends BaseColumns { 40 | /* default */ static final String PATH = "device"; 41 | public static final String MODEL = "model"; 42 | public static final String NICKNAME = "nickname"; 43 | public static final String MEMORY_MB = "memory_mb"; 44 | 45 | public static final String DISPLAY_SIZE_INCHES = 46 | "display_size_inches"; 47 | 48 | public static final String MANUFACTURER_ID = "manufacturer_id"; 49 | 50 | public static final Uri CONTENT_URI = 51 | Uri.withAppendedPath(AUTHORITY_URI, PATH); 52 | } 53 | 54 | public interface Manufacturer extends BaseColumns { 55 | /* default */ static final String PATH = "manufacturer"; 56 | public static final String SHORT_NAME = "short_name"; 57 | public static final String LONG_NAME = "long_name"; 58 | 59 | public static final Uri CONTENT_URI = 60 | Uri.withAppendedPath(AUTHORITY_URI, PATH); 61 | } 62 | 63 | public interface DeviceManufacturer extends Device, Manufacturer { 64 | /* default */ static final String PATH = "device-manufacturer"; 65 | public static final String DEVICE_ID = "device_id"; 66 | public static final String MANUFACTURER_ID = "manufacturer_id"; 67 | 68 | public static final Uri CONTENT_URI = 69 | Uri.withAppendedPath(AUTHORITY_URI, PATH); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/sync/Authenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.sync; 18 | 19 | import android.accounts.AbstractAccountAuthenticator; 20 | import android.accounts.Account; 21 | import android.accounts.AccountAuthenticatorResponse; 22 | import android.accounts.NetworkErrorException; 23 | import android.content.Context; 24 | import android.os.Bundle; 25 | 26 | /** 27 | * Stub Authenticator for use with the SyncAdapter. 28 | * 29 | * @author Adam Stroud <adam.stroud@gmail.com> 30 | */ 31 | public class Authenticator extends AbstractAccountAuthenticator { 32 | public Authenticator(Context context) { 33 | super(context); 34 | } 35 | 36 | @Override 37 | public Bundle editProperties(AccountAuthenticatorResponse response, 38 | String accountType) { 39 | throw new UnsupportedOperationException("Not yet implemented"); 40 | } 41 | 42 | @Override 43 | public Bundle addAccount(AccountAuthenticatorResponse response, 44 | String accountType, 45 | String authTokenType, 46 | String[] requiredFeatures, 47 | Bundle options) throws NetworkErrorException { 48 | throw new UnsupportedOperationException("Not yet implemented"); 49 | } 50 | 51 | @Override 52 | public Bundle confirmCredentials(AccountAuthenticatorResponse response, 53 | Account account, 54 | Bundle options) 55 | throws NetworkErrorException { 56 | throw new UnsupportedOperationException("Not yet implemented"); 57 | } 58 | 59 | @Override 60 | public Bundle getAuthToken(AccountAuthenticatorResponse response, 61 | Account account, 62 | String authTokenType, 63 | Bundle options) 64 | throws NetworkErrorException { 65 | throw new UnsupportedOperationException("Not yet implemented"); 66 | } 67 | 68 | @Override 69 | public String getAuthTokenLabel(String authTokenType) { 70 | throw new UnsupportedOperationException("Not yet implemented"); 71 | } 72 | 73 | @Override 74 | public Bundle updateCredentials(AccountAuthenticatorResponse response, 75 | Account account, 76 | String authTokenType, 77 | Bundle options) 78 | throws NetworkErrorException { 79 | throw new UnsupportedOperationException("Not yet implemented"); 80 | } 81 | 82 | @Override 83 | public Bundle hasFeatures(AccountAuthenticatorResponse response, 84 | Account account, 85 | String[] features) 86 | throws NetworkErrorException { 87 | throw new UnsupportedOperationException("Not yet implemented"); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/sync/AuthenticatorService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.sync; 18 | 19 | import android.app.Service; 20 | import android.content.Intent; 21 | import android.os.IBinder; 22 | 23 | public class AuthenticatorService extends Service { 24 | private Authenticator authenticator; 25 | 26 | public AuthenticatorService() { 27 | } 28 | 29 | @Override 30 | public void onCreate() { 31 | super.onCreate(); 32 | authenticator = new Authenticator(this); 33 | } 34 | 35 | @Override 36 | public IBinder onBind(Intent intent) { 37 | return authenticator.getIBinder(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/me/adamstroud/devicedatabase/sync/SyncService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Adam Stroud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package me.adamstroud.devicedatabase.sync; 18 | 19 | import android.app.Service; 20 | import android.content.Intent; 21 | import android.os.IBinder; 22 | import android.support.annotation.Nullable; 23 | 24 | /** 25 | * Binds the {@link SyncAdapter} to the sync framework. 26 | * 27 | * @author Adam Stroud <adam.stroud@gmail.com> 28 | */ 29 | public class SyncService extends Service { 30 | private static SyncAdapter syncAdapter = null; 31 | 32 | @Override 33 | public void onCreate() { 34 | super.onCreate(); 35 | 36 | synchronized (SyncService.class) { 37 | syncAdapter = new SyncAdapter(getApplicationContext(), true); 38 | } 39 | } 40 | 41 | @Nullable 42 | @Override 43 | public IBinder onBind(Intent intent) { 44 | return syncAdapter.getSyncAdapterBinder(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-hdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_done_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-hdpi/ic_done_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_search_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-hdpi/ic_search_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-mdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_done_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-mdpi/ic_done_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_search_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-mdpi/ic_search_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-xhdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_done_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-xhdpi/ic_done_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_search_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-xhdpi/ic_search_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_done_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-xxhdpi/ic_done_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_done_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-xxxhdpi/ic_done_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-database-best-practices/device-database/3423589b8d0810b253fa7829e3c25b132cc0f74c/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_add_device.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 20 | 21 | 22 | 23 | 29 | 33 | 34 | 35 | 36 | 41 | 44 | 45 | 46 | 51 | 54 | 55 | 56 | 61 | 65 | 66 | 67 | 72 | 76 | 77 | 78 |