From 20ef6500dc0b6d84905d92d8469feb6ff0ac502d Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 30 May 2017 17:26:14 -0700 Subject: migrate to dbflow --- .idea/misc.xml | 16 ---- app/build.gradle | 23 +++++- app/src/main/AndroidManifest.xml | 2 +- .../main/kotlin/com/pitchedapps/frost/FrostApp.kt | 12 +-- .../kotlin/com/pitchedapps/frost/MainActivity.kt | 14 ++-- .../kotlin/com/pitchedapps/frost/StartActivity.kt | 2 +- .../com/pitchedapps/frost/dbflow/CookiesDb.kt | 60 +++++++++++++++ .../com/pitchedapps/frost/dbflow/FbTabsDb.kt | 85 +++++++++++++++++++++ .../com/pitchedapps/frost/facebook/UrlData.kt | 42 +--------- .../pitchedapps/frost/facebook/retro/FrostApi.kt | 20 ++++- .../com/pitchedapps/frost/facebook/retro/IFrost.kt | 22 +++++- .../kotlin/com/pitchedapps/frost/utils/DbUtils.kt | 33 ++++++++ .../main/kotlin/com/pitchedapps/frost/utils/L.kt | 11 +-- .../com/pitchedapps/frost/utils/LazyResettable.kt | 51 +++++++++++++ .../kotlin/com/pitchedapps/frost/utils/Prefs.kt | 5 +- .../kotlin/com/pitchedapps/frost/utils/Realm.kt | 18 ----- .../com/pitchedapps/frost/views/FrostWebView.kt | 8 +- .../com/pitchedapps/frost/views/LoginWebView.kt | 11 +-- app/src/main/res/menu/menu_main.xml | 5 ++ app/src/main/res/mipmap-hdpi/ic_launcher_round.png | Bin 4208 -> 3924 bytes .../main/res/mipmap-xhdpi/ic_launcher_round.png | Bin 6114 -> 5972 bytes .../main/res/mipmap-xxhdpi/ic_launcher_round.png | Bin 10056 -> 9569 bytes .../main/res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 14696 -> 12529 bytes .../com/pitchedapps/frost/ExampleUnitTest.java | 17 ----- .../kotlin/com/pitchedapps/frost/BaseUnitTest.kt | 25 ++++++ .../kotlin/com/pitchedapps/frost/DbFlowTestRule.kt | 38 +++++++++ .../frost/ImmediateTransactionManager.kt | 38 +++++++++ .../kotlin/com/pitchedapps/frost/TestDatabase.kt | 21 +++++ build.gradle | 1 - gradle.properties | 5 ++ 30 files changed, 448 insertions(+), 137 deletions(-) create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/dbflow/FbTabsDb.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/utils/DbUtils.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/utils/LazyResettable.kt delete mode 100644 app/src/main/kotlin/com/pitchedapps/frost/utils/Realm.kt delete mode 100644 app/src/test/java/com/pitchedapps/frost/ExampleUnitTest.java create mode 100644 app/src/test/kotlin/com/pitchedapps/frost/BaseUnitTest.kt create mode 100644 app/src/test/kotlin/com/pitchedapps/frost/DbFlowTestRule.kt create mode 100644 app/src/test/kotlin/com/pitchedapps/frost/ImmediateTransactionManager.kt create mode 100644 app/src/test/kotlin/com/pitchedapps/frost/TestDatabase.kt diff --git a/.idea/misc.xml b/.idea/misc.xml index 085136f8..7158618b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -27,22 +27,6 @@ - - - - - - - - - - - Android - - - - - diff --git a/app/build.gradle b/app/build.gradle index 92167ac7..4fffb72c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,12 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'realm-android' android { compileSdkVersion Integer.parseInt(project.TARGET_SDK) buildToolsVersion project.BUILD_TOOLS defaultConfig { - applicationId "${project.APP_GROUP}." + project.APP_ID.toLowerCase() + ".sample" + applicationId "${project.APP_GROUP}." + project.APP_ID.toLowerCase() minSdkVersion Integer.parseInt(project.MIN_SDK) targetSdkVersion Integer.parseInt(project.TARGET_SDK) versionCode Integer.parseInt(project.VERSION_CODE) @@ -57,6 +56,18 @@ dependencies { //Dialog compile "com.afollestad.material-dialogs:core:${MD}" + compile "com.github.Raizlabs.DBFlow:dbflow:${DBFLOW}" + compile "com.github.Raizlabs.DBFlow:dbflow-core:${DBFLOW}" + annotationProcessor "com.github.Raizlabs.DBFlow:dbflow-processor:${DBFLOW}" + kapt "com.github.Raizlabs.DBFlow:dbflow-processor:${DBFLOW}" + compile "com.github.Raizlabs.DBFlow:dbflow-kotlinextensions:${DBFLOW}" + compile "com.github.Raizlabs.DBFlow:dbflow-rx2:${DBFLOW}" + compile "com.github.Raizlabs.DBFlow:dbflow-rx2-kotlinextensions:${DBFLOW}" + compile "com.github.Raizlabs.DBFlow:dbflow-sqlcipher:${DBFLOW}" + compile "net.zetetic:android-database-sqlcipher:${SQL_CIPHER}@aar" + + testCompile "org.robolectric:robolectric:${ROBOELECTRIC}" + //Icons compile "com.mikepenz:iconics-core:${ICONICS}@aar" compile "com.mikepenz:google-material-typeface:${IICON_GOOGLE}.original@aar" @@ -89,4 +100,12 @@ dependencies { compile "com.github.bumptech.glide:glide:${GLIDE}" annotationProcessor "com.github.bumptech.glide:compiler:${GLIDE}" + + compile "com.google.auto.value:auto-value:${AUTO}" + annotationProcessor "com.google.auto.value:auto-value:${AUTO}" + annotationProcessor "com.ryanharter.auto.value:auto-value-parcel:${AUTO_VALUE_PARCEL}" +} + +kapt { + generateStubs = true } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ff98de24..793e3983 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,7 +21,7 @@ Changelog.show(this) + R.id.action_call -> frostApi.me().enqueueFrost { call, response -> L.e(response.toString())} else -> return super.onOptionsItemSelected(item) } return true diff --git a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt index 390efb1a..e8c65be3 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt @@ -11,7 +11,7 @@ class StartActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - startActivity(Intent(this, LoginActivity::class.java)) + startActivity(Intent(this, MainActivity::class.java)) finish() } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt new file mode 100644 index 00000000..ab39118a --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt @@ -0,0 +1,60 @@ +package com.pitchedapps.frost.dbflow + +import com.raizlabs.android.dbflow.annotation.Database +import com.raizlabs.android.dbflow.annotation.ForeignKey +import com.raizlabs.android.dbflow.annotation.PrimaryKey +import com.raizlabs.android.dbflow.annotation.Table +import com.raizlabs.android.dbflow.structure.BaseModel +import okhttp3.Cookie +import java.io.Serializable + +/** + * Created by Allan Wang on 2017-05-30. + */ + +@Database(name = CookiesDb.NAME, version = CookiesDb.VERSION) +object CookiesDb { + const val NAME = "Cookies" + const val VERSION = 1 +} + +//@Database(name = CookieDb.NAME, version = CookieDb.VERSION) +//object CookieDb { +// const val NAME = "Cookie" +// const val VERSION = 1 +//} + +@Table(database = CookiesDb::class, allFields = true) +data class CookieModel(@PrimaryKey var name: String, + var value: String, + var expiresAt: Long, + var domain: String, + var path: String, + var secure: Boolean, + var httpOnly: Boolean) { + + constructor(cookie: Cookie) : this(cookie.name(), cookie.value(), cookie.expiresAt(), cookie.domain(), cookie.path(), cookie.secure(), cookie.httpOnly()) + constructor() : this("", "", 0L, "", "", false, false) + + fun toCookie(): Cookie { + val builder = Cookie.Builder().name(name).value(value).expiresAt(expiresAt).domain(domain).path(path) + if (secure) builder.secure() + if (httpOnly) builder.httpOnly() + return builder.build() + } + + fun isSecure() = secure + fun isHttpOnly() = httpOnly +} + +//class CookieList(val cookies: List) +//class CookieDbList(val cookies: List) + +//@com.raizlabs.android.dbflow.annotation.TypeConverter +//class CookieTypeConverter() : TypeConverter() { +// override fun getModelValue(data: CookieDbList): CookieList = CookieList(data.cookies.map { it.toCookie() }) +// override fun getDBValue(model: CookieList): CookieDbList = CookieDbList(model.cookies.map { CookieDb(it) }) +//} + +@Table(database = CookiesDb::class) +data class Cookies(@PrimaryKey var url: String = "", @ForeignKey var cookie: CookieModel = CookieModel()) : BaseModel(), Serializable \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/FbTabsDb.kt b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/FbTabsDb.kt new file mode 100644 index 00000000..05dac758 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/FbTabsDb.kt @@ -0,0 +1,85 @@ +package com.pitchedapps.frost.dbflow + +import android.content.Context +import android.support.annotation.StringRes +import com.mikepenz.community_material_typeface_library.CommunityMaterial +import com.mikepenz.google_material_typeface_library.GoogleMaterial +import com.mikepenz.iconics.typeface.IIcon +import com.mikepenz.material_design_iconic_typeface_library.MaterialDesignIconic +import com.pitchedapps.frost.R +import com.pitchedapps.frost.facebook.FB_KEY +import com.raizlabs.android.dbflow.annotation.Database +import com.raizlabs.android.dbflow.annotation.ForeignKey +import com.raizlabs.android.dbflow.annotation.PrimaryKey +import com.raizlabs.android.dbflow.annotation.Table +import com.raizlabs.android.dbflow.kotlinextensions.from +import com.raizlabs.android.dbflow.sql.language.SQLite +import com.raizlabs.android.dbflow.structure.BaseModel +import java.io.Serializable + +/** + * Created by Allan Wang on 2017-05-30. + */ + +@Database(name = FbTabsDb.NAME, version = FbTabsDb.VERSION) +object FbTabsDb { + const val NAME = "FrostTabs" + const val VERSION = 1 +} + +data class FbTab(val title: String, val icon: IIcon, val url: String) + +@Table(database = FbTabsDb::class, allFields = true) +data class FbTabModel( + var title: String = "Home", + @ForeignKey var icon: IIconModel = IIconModel(), + @PrimaryKey var url: String = "" +) : BaseModel(), Serializable { + constructor(fbTab: FbTab) : this(fbTab.title, IIconModel(fbTab.icon), fbTab.url) + + fun toFbTab() = FbTab(title, icon.toIIcon(), url) +} + +@Table(database = FbTabsDb::class, allFields = true) +data class IIconModel(var type: Int = -1, @PrimaryKey var name: String = "") : BaseModel(), Serializable { + constructor(icon: IIcon) : this(when (icon) { + is CommunityMaterial -> 0 + is GoogleMaterial -> 1 + is MaterialDesignIconic -> 2 + else -> -1 + }, icon.toString()) + + fun toIIcon(): IIcon = when (type) { + 0 -> CommunityMaterial.Icon.valueOf(name) + 1 -> GoogleMaterial.Icon.valueOf(name) + 2 -> MaterialDesignIconic.Icon.valueOf(name) + else -> CommunityMaterial.Icon.cmd_newspaper + } +} + +enum class FbUrl(@StringRes val titleId: Int, val icon: IIcon, val url: String) { + LOGIN(R.string.feed, CommunityMaterial.Icon.cmd_newspaper, "https://www.facebook.com/v2.9/dialog/oauth?client_id=${FB_KEY}&redirect_uri=https://touch.facebook.com/&response_type=token,granted_scopes"), + FEED(R.string.feed, CommunityMaterial.Icon.cmd_newspaper, "https://touch.facebook.com/"), + PROFILE(R.string.profile, CommunityMaterial.Icon.cmd_account, "https://touch.facebook.com/me/"), + EVENTS(R.string.events, GoogleMaterial.Icon.gmd_event, "https://touch.facebook.com/events/upcoming"), + FRIENDS(R.string.friends, GoogleMaterial.Icon.gmd_people, "https://touch.facebook.com/friends/center/requests/"), + MESSAGES(R.string.messages, MaterialDesignIconic.Icon.gmi_comments, "https://touch.facebook.com/messages"), + NOTIFICATIONS(R.string.notifications, MaterialDesignIconic.Icon.gmi_globe, "https://touch.facebook.com/notifications"); + + fun tabInfo(c: Context) = FbTab(c.getString(titleId), icon, url) +} + +//BOOKMARKS("https://touch.facebook.com/bookmarks"), +//SEARCH("https://touch.facebook.com/search"), + +fun loadFbTabs(c: Context): List { + val tabs = SQLite.select() + .from(FbTabModel::class) + .queryList() + if (tabs.isNotEmpty()) return tabs.map { it.toFbTab() } + return listOf(FbUrl.FEED, FbUrl.MESSAGES, FbUrl.FRIENDS, FbUrl.NOTIFICATIONS).map { it.tabInfo(c) } +} + +fun List.saveAsync() { + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/UrlData.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/UrlData.kt index d5f9db6e..c5c2a86b 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/UrlData.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/UrlData.kt @@ -7,12 +7,7 @@ import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.material_design_iconic_typeface_library.MaterialDesignIconic import com.pitchedapps.frost.R -import com.pitchedapps.frost.utils.RealmFiles -import com.pitchedapps.frost.utils.realm -import io.realm.Realm -import io.realm.RealmList -import io.realm.RealmObject -import io.realm.annotations.PrimaryKey +import com.pitchedapps.frost.dbflow.FbTab /** * Created by Allan Wang on 2017-05-29. @@ -30,37 +25,4 @@ enum class FbUrl(@StringRes val titleId: Int, val icon: IIcon, val url: String) } //BOOKMARKS("https://touch.facebook.com/bookmarks"), -//SEARCH("https://touch.facebook.com/search"), - -class FbTab(var title: String, var icon: IIcon, var url: String) { - constructor(realm: FbTabRealm) : this(realm.title, when (realm.iconCategory) { - 0 -> GoogleMaterial.Icon.valueOf(realm.iconString) - 1 -> CommunityMaterial.Icon.valueOf(realm.iconString) - 2 -> MaterialDesignIconic.Icon.valueOf(realm.iconString) - else -> GoogleMaterial.Icon.gmd_error - }, realm.url) -} - -open class FbTabRealm(var title: String, var iconCategory: Int, var iconString: String, @PrimaryKey var url: String) : RealmObject() { - constructor(tab: FbTab) : this(tab.title, when (tab.icon.typeface) { - is GoogleMaterial -> 0 - is CommunityMaterial -> 1 - is MaterialDesignIconic -> 2 - else -> -1 - }, tab.icon.toString(), tab.url) - - constructor() : this("", -1, "", "") -} - -fun List.save() { - val list = RealmList(*this.map { FbTabRealm(it) }.toTypedArray()) - realm(RealmFiles.TABS, Realm.Transaction { it.copyToRealmOrUpdate(list) }) -} - -fun loadFbTab(c: Context): List { - val realmList = mutableListOf() - realm(RealmFiles.TABS, Realm.Transaction { it.copyFromRealm(realmList) }) - if (realmList.isNotEmpty()) return realmList.map { FbTab(it) } - return listOf(FbUrl.FEED, FbUrl.MESSAGES, FbUrl.NOTIFICATIONS).map { it.tabInfo(c) } -} - +//SEARCH("https://touch.facebook.com/search"), \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostApi.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostApi.kt index 746bf0df..f82c041c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostApi.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostApi.kt @@ -4,9 +4,9 @@ import android.content.Context import com.facebook.stetho.okhttp3.StethoInterceptor import com.google.gson.GsonBuilder import com.pitchedapps.frost.BuildConfig +import com.pitchedapps.frost.utils.L import io.reactivex.schedulers.Schedulers -import okhttp3.Cache -import okhttp3.OkHttpClient +import okhttp3.* import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory @@ -20,7 +20,7 @@ import java.io.File */ object FrostApi { - internal lateinit var frostApi: IFrost + lateinit var frostApi: IFrost operator fun invoke(context: Context) { val cacheDir = File(context.cacheDir, "responses") @@ -29,6 +29,18 @@ object FrostApi { val client = OkHttpClient.Builder() .addInterceptor(FrostInterceptor(context)) + .cookieJar(object : CookieJar { + override fun saveFromResponse(url: HttpUrl, cookies: List) { + L.e("COOKIES") + L.e(url.toString()) + cookies.forEach { c -> L.e(c.toString()) } + } + + override fun loadForRequest(url: HttpUrl): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + }) .cache(cache) @@ -42,7 +54,7 @@ object FrostApi { val gson = GsonBuilder().setLenient() val retrofit = Retrofit.Builder() - .baseUrl("https://graph.facebook.com/") + .baseUrl("https://touch.facebook.com/") .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) .addConverterFactory(GsonConverterFactory.create(gson.create())) .client(client.build()) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/IFrost.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/IFrost.kt index 6c50fa74..b9048fe0 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/IFrost.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/IFrost.kt @@ -1,7 +1,11 @@ package com.pitchedapps.frost.facebook.retro import com.pitchedapps.frost.facebook.token +import com.pitchedapps.frost.utils.L +import okhttp3.ResponseBody import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Query @@ -11,6 +15,22 @@ import retrofit2.http.Query interface IFrost { @GET("me") - fun me(@Query(ACCESS_TOKEN) accessToken: String? = token): Call + fun me(): Call + +} + +fun Call.enqueueFrost(success: (call: Call, response: Response) -> Unit) { + this.enqueue(object : Callback { + override fun onFailure(call: Call?, t: Throwable?) { + L.e("Frost enqueue error") + } + + override fun onResponse(call: Call, response: Response) { + if (response.isSuccessful && !call.isCanceled) + success.invoke(call, response) + else + L.e("Frost response received but not successful") + } + }) } \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/DbUtils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/DbUtils.kt new file mode 100644 index 00000000..dc16f6cc --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/DbUtils.kt @@ -0,0 +1,33 @@ +package com.pitchedapps.frost.utils + +import android.content.Context +import com.raizlabs.android.dbflow.config.FlowManager +import com.raizlabs.android.dbflow.kotlinextensions.processInTransactionAsync +import com.raizlabs.android.dbflow.structure.BaseModel +import com.raizlabs.android.dbflow.structure.database.transaction.Transaction + +/** + * Created by Allan Wang on 2017-05-30. + */ +object DbUtils { + + fun db(name: String) = FlowManager.getDatabase(name) + fun dbName(name: String) = "$name.db" + + inline fun replace( + context: Context, dbName: String, type: Class, data: List, + crossinline callback: ((successful: Boolean) -> Unit)) { + db(dbName).reset(context) + data.processInTransactionAsync({ + t, databaseWrapper -> + t.save(databaseWrapper) + }, + Transaction.Success { + callback.invoke(true) + }, + Transaction.Error { _, error -> + L.e(error.message ?: "DbReplace error") + callback.invoke(false) + }) + } +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/L.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/L.kt index 0151b0ae..fe1fdc7c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/L.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/L.kt @@ -4,16 +4,13 @@ import android.util.Log import timber.log.Timber - /** * Created by Allan Wang on 2017-05-28. */ -class L { - companion object { - val TAG = "Frost: %s" - fun e(s: String) = Timber.e(TAG, s) - fun d(s: String) = Timber.d(TAG, s) - } +object L { + val TAG = "Frost: %s" + fun e(s: String) = Timber.e(TAG, s) + fun d(s: String) = Timber.d(TAG, s) } internal class CrashReportingTree : Timber.Tree() { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/LazyResettable.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/LazyResettable.kt new file mode 100644 index 00000000..c8365f5c --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/LazyResettable.kt @@ -0,0 +1,51 @@ +package com.pitchedapps.frost.utils + +import java.io.Serializable +import kotlin.reflect.KProperty + +/** + * Created by Allan Wang on 2017-05-30. + * + * Lazy delegate that can be invalidated if needed + * https://stackoverflow.com/a/37294840/4407321 + */ +private object UNINITIALIZED + +fun lazyResettable(initializer: () -> T): LazyResettable = LazyResettable(initializer) + +class LazyResettable(private val initializer: () -> T, lock: Any? = null) : Lazy, Serializable { + @Volatile private var _value: Any = UNINITIALIZED + private val lock = lock ?: this + + fun invalidate() { + _value = UNINITIALIZED + } + + override val value: T + get() { + val _v1 = _value + if (_v1 !== UNINITIALIZED) + @Suppress("UNCHECKED_CAST") + return _v1 as T + + return synchronized(lock) { + val _v2 = _value + if (_v2 !== UNINITIALIZED) { + @Suppress("UNCHECKED_CAST") + _v2 as T + } else { + val typedValue = initializer() + _value = typedValue + typedValue + } + } + } + + override fun isInitialized(): Boolean = _value !== UNINITIALIZED + + override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet." + + operator fun setValue(any: Any, property: KProperty<*>, t: T) { + _value = t + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt index ad222145..710da845 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt @@ -17,6 +17,7 @@ object Prefs { lateinit private var c: Context operator fun invoke(c: Context) { this.c = c + lastActive = 0 } private val sp: SharedPreferences by lazy { c.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) } @@ -25,10 +26,6 @@ object Prefs { get() = sp.getLong(LAST_ACTIVE, -1) set(value) = set(LAST_ACTIVE, System.currentTimeMillis()) - init { - lastActive = 0 - } - private fun set(key: String, value: Boolean) = sp.edit().putBoolean(key, value).apply() private fun set(key: String, value: Int) = sp.edit().putInt(key, value).apply() private fun set(key: String, value: Long) = sp.edit().putLong(key, value).apply() diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Realm.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Realm.kt deleted file mode 100644 index 1eded8c9..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Realm.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.pitchedapps.frost.utils - -import io.realm.Realm -import io.realm.RealmConfiguration - -/** - * Created by Allan Wang on 2017-05-29. - */ -@JvmOverloads fun realm(name: String = RealmFiles.main, transaction: Realm.Transaction) { - val realm = Realm.getInstance(RealmConfiguration.Builder().name(name).build()) - realm.executeTransaction(transaction) - realm.close() -} - -object RealmFiles { - val main = "frost.realm" - val TABS = "tabs.realm" -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt index f09887d8..a59fa3b1 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt @@ -10,10 +10,7 @@ import android.support.v4.view.ViewCompat import android.util.AttributeSet import android.view.MotionEvent import android.view.View -import android.webkit.WebResourceError -import android.webkit.WebResourceRequest -import android.webkit.WebView -import android.webkit.WebViewClient +import android.webkit.* import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.ObservableContainer import io.reactivex.subjects.BehaviorSubject @@ -67,8 +64,9 @@ class FrostWebView @JvmOverloads constructor( override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) observable.onNext(WebStatus.LOADED) -// CookieManager.getInstance().flush() + val cookie = CookieManager.getInstance().getCookie(url) L.d("Loaded $url") + L.d("Cookie $cookie") } }) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/LoginWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/LoginWebView.kt index 7d1948fe..17557b4d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/LoginWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/LoginWebView.kt @@ -14,6 +14,7 @@ import com.facebook.AccessToken import com.pitchedapps.frost.facebook.FB_KEY import com.pitchedapps.frost.facebook.retro.FrostApi.frostApi import com.pitchedapps.frost.facebook.retro.Me +import com.pitchedapps.frost.facebook.retro.enqueueFrost import com.pitchedapps.frost.utils.L import retrofit2.Call import retrofit2.Callback @@ -63,15 +64,9 @@ class LoginWebView @JvmOverloads constructor( fun saveAccessToken(accessToken: String, expiresIn: String, grantedScopes: String?, deniedScopes: String?) { L.d("Granted $grantedScopes") L.d("Denied $deniedScopes") - frostApi.me(accessToken).enqueue(object : Callback { - override fun onFailure(call: Call?, t: Throwable?) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } + frostApi.me().enqueueFrost { call, response -> - override fun onResponse(call: Call, response: Response) { - AccessToken.setCurrentAccessToken(AccessToken(accessToken, FB_KEY.toString(), response.body().id, null, null, null, null, null)) - } - }) + } } diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index b4140da4..4edd60de 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -12,4 +12,9 @@ android:orderInCategory="200" android:title="@string/changelog" app:showAsAction="never" /> + diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index 9a078e3e..618290a8 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 3af2608a..44c43db9 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index 9bec2e62..120ecda4 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index 34947cd6..ce8ca288 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/test/java/com/pitchedapps/frost/ExampleUnitTest.java b/app/src/test/java/com/pitchedapps/frost/ExampleUnitTest.java deleted file mode 100644 index a702863a..00000000 --- a/app/src/test/java/com/pitchedapps/frost/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.pitchedapps.frost; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() throws Exception { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/app/src/test/kotlin/com/pitchedapps/frost/BaseUnitTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/BaseUnitTest.kt new file mode 100644 index 00000000..55208aae --- /dev/null +++ b/app/src/test/kotlin/com/pitchedapps/frost/BaseUnitTest.kt @@ -0,0 +1,25 @@ +package com.pitchedapps.frost + +import android.content.Context +import android.os.Build +import org.junit.Rule +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config + +/** + * Created by Allan Wang on 2017-05-30. + */ +@RunWith(RobolectricTestRunner::class) +@Config(constants = BuildConfig::class, sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), + assetDir = "build/intermediates/classes/test/") +abstract class BaseUnitTest { + + @JvmField + @Rule + var dblflowTestRule = DBFlowTestRule.create() + + val context: Context + get() = RuntimeEnvironment.application +} diff --git a/app/src/test/kotlin/com/pitchedapps/frost/DbFlowTestRule.kt b/app/src/test/kotlin/com/pitchedapps/frost/DbFlowTestRule.kt new file mode 100644 index 00000000..aaf38a0f --- /dev/null +++ b/app/src/test/kotlin/com/pitchedapps/frost/DbFlowTestRule.kt @@ -0,0 +1,38 @@ +package com.pitchedapps.frost + +import com.raizlabs.android.dbflow.config.DatabaseConfig +import com.raizlabs.android.dbflow.config.FlowConfig +import com.raizlabs.android.dbflow.config.FlowManager +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement +import org.robolectric.RuntimeEnvironment + +/** + * Created by Allan Wang on 2017-05-30. + */ + +class DBFlowTestRule : TestRule { + + override fun apply(base: Statement, description: Description): Statement { + return object : Statement() { + + @Throws(Throwable::class) + override fun evaluate() { + FlowManager.init(FlowConfig.Builder(RuntimeEnvironment.application) + .addDatabaseConfig(DatabaseConfig.Builder(TestDatabase::class.java) + .transactionManagerCreator(::ImmediateTransactionManager) + .build()).build()) + try { + base.evaluate() + } finally { + FlowManager.destroy() + } + } + } + } + + companion object { + fun create() = DBFlowTestRule() + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/com/pitchedapps/frost/ImmediateTransactionManager.kt b/app/src/test/kotlin/com/pitchedapps/frost/ImmediateTransactionManager.kt new file mode 100644 index 00000000..f3d17e76 --- /dev/null +++ b/app/src/test/kotlin/com/pitchedapps/frost/ImmediateTransactionManager.kt @@ -0,0 +1,38 @@ +package com.pitchedapps.frost + +import com.raizlabs.android.dbflow.config.DatabaseDefinition +import com.raizlabs.android.dbflow.runtime.BaseTransactionManager +import com.raizlabs.android.dbflow.structure.database.transaction.ITransactionQueue +import com.raizlabs.android.dbflow.structure.database.transaction.Transaction +/** + * Created by Allan Wang on 2017-05-30. + */ +class ImmediateTransactionManager(databaseDefinition: DatabaseDefinition) + : BaseTransactionManager(ImmediateTransactionQueue(), databaseDefinition) + + +class ImmediateTransactionQueue : ITransactionQueue { + + override fun add(transaction: Transaction?) { + if (transaction != null) { + transaction.newBuilder() + .runCallbacksOnSameThread(true) + .build() + .executeSync() + } + } + + override fun cancel(transaction: Transaction?) { + + } + + override fun startIfNotAlive() { + } + + override fun cancel(name: String?) { + } + + override fun quit() { + } + +} \ No newline at end of file diff --git a/app/src/test/kotlin/com/pitchedapps/frost/TestDatabase.kt b/app/src/test/kotlin/com/pitchedapps/frost/TestDatabase.kt new file mode 100644 index 00000000..328233ac --- /dev/null +++ b/app/src/test/kotlin/com/pitchedapps/frost/TestDatabase.kt @@ -0,0 +1,21 @@ +package com.pitchedapps.frost + +import com.raizlabs.android.dbflow.annotation.Database +import com.raizlabs.android.dbflow.annotation.Migration +import com.raizlabs.android.dbflow.sql.migration.UpdateTableMigration + +/** + * Created by Allan Wang on 2017-05-30. + */ + +/** + * Description: + */ +@Database(version = TestDatabase.VERSION, name = TestDatabase.NAME) +object TestDatabase { + + const val VERSION = 1 + + const val NAME = "TestDatabase" + +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index ec3796d5..6fafc759 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,6 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:2.3.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "io.realm:realm-gradle-plugin:3.3.1" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/gradle.properties b/gradle.properties index d3d96b32..00849d8a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -36,4 +36,9 @@ STETHO=1.4.2 ANKO=0.10.0 GLIDE=4.0.0-RC0 RETROFIT=2.2.0 +DBFLOW=4.0.2 +SQL_CIPHER=3.5.7 OKHTTP_INTERCEPTOR=3.6.0 +ROBOELECTRIC=3.3.2 +AUTO=1.4.1 +AUTO_VALUE_PARCEL=0.2.5 \ No newline at end of file -- cgit v1.2.3