├── app ├── .gitignore ├── src │ ├── main │ │ ├── assets │ │ │ ├── evernote.properties │ │ │ └── www │ │ │ │ └── template.html │ │ ├── res │ │ │ ├── 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 │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── menu │ │ │ │ └── menu_main.xml │ │ │ ├── values-w820dp │ │ │ │ └── dimens.xml │ │ │ ├── layout │ │ │ │ ├── main_activity.xml │ │ │ │ ├── fragment_basewebview.xml │ │ │ │ ├── show_site_list_item.xml │ │ │ │ ├── activity_save_tag.xml │ │ │ │ ├── view_web_progress.xml │ │ │ │ ├── activity_main.xml │ │ │ │ └── link_list_fragment.xml │ │ │ └── drawable │ │ │ │ └── drawable_webview_progress.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── kotlinchina │ │ │ │ ├── smallpockets │ │ │ │ ├── service │ │ │ │ │ ├── ClipboardService.kt │ │ │ │ │ ├── IParseDom.kt │ │ │ │ │ ├── ShareService.kt │ │ │ │ │ ├── HttpService.kt │ │ │ │ │ ├── IDataBaseStore.kt │ │ │ │ │ └── impl │ │ │ │ │ │ ├── JxPathParseDom.kt │ │ │ │ │ │ ├── DebugCacheClipboardService.kt │ │ │ │ │ │ ├── WebViewClientHttpService.kt │ │ │ │ │ │ ├── VolleyHttpService.kt │ │ │ │ │ │ ├── CacheClipboardService.kt │ │ │ │ │ │ ├── EvernoteShareService.kt │ │ │ │ │ │ ├── RealmStore.kt │ │ │ │ │ │ └── CalendarService.kt │ │ │ │ ├── model │ │ │ │ │ ├── Tag.kt │ │ │ │ │ ├── db │ │ │ │ │ │ ├── RealmTag.kt │ │ │ │ │ │ └── RealmLink.kt │ │ │ │ │ ├── Link.kt │ │ │ │ │ └── impl │ │ │ │ │ │ ├── CoreTag.kt │ │ │ │ │ │ ├── LinkListMapper.kt │ │ │ │ │ │ └── CoreLink.kt │ │ │ │ ├── view │ │ │ │ │ ├── IMainView.kt │ │ │ │ │ ├── ILinkListView.kt │ │ │ │ │ ├── Fragment │ │ │ │ │ │ └── BaseWebViewFragment.kt │ │ │ │ │ └── impl │ │ │ │ │ │ ├── SaveTagDialog.kt │ │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ │ └── LinkListFragment.kt │ │ │ │ ├── presenter │ │ │ │ │ ├── IMainPresenter.kt │ │ │ │ │ ├── ILinkListPresenter.kt │ │ │ │ │ └── impl │ │ │ │ │ │ ├── MainPresenter.kt │ │ │ │ │ │ └── LinkListPresenter.kt │ │ │ │ ├── transform │ │ │ │ │ ├── ILinksToHTML.kt │ │ │ │ │ └── impl │ │ │ │ │ │ └── LinksToHTML.kt │ │ │ │ ├── utils │ │ │ │ │ ├── PocketWebViewClient.kt │ │ │ │ │ ├── PocketWebChromeClient.kt │ │ │ │ │ ├── DoubClickExitHelper.kt │ │ │ │ │ └── AppManager.kt │ │ │ │ ├── application │ │ │ │ │ └── PocketApplication.kt │ │ │ │ ├── adapter │ │ │ │ │ └── ShowSiteListAdapter.kt │ │ │ │ └── widget │ │ │ │ │ └── CusTomProgressWebView.kt │ │ │ │ └── htmlengine │ │ │ │ ├── protocol │ │ │ │ ├── IHTMLTemplateLoader.kt │ │ │ │ └── IHTMLTemplateRender.kt │ │ │ │ ├── impl │ │ │ │ ├── HTMLTemplateLoader.kt │ │ │ │ └── HTMLTemplateRender.kt │ │ │ │ └── HTMLEngineManager.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── kotlinchina │ │ │ └── smallpockets │ │ │ ├── ExampleUnitTest.java │ │ │ ├── service │ │ │ └── impl │ │ │ │ └── LinkListMapperKtTest.kt │ │ │ └── presenter │ │ │ ├── LinkListPresenterTest.kt │ │ │ └── MainPresenterTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── kotlinchina │ │ └── smallpockets │ │ └── ApplicationTest.java ├── keystore │ └── release.jks ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── screenshot ├── 1k.png ├── 2k.png ├── 3k.png ├── 4k.png └── 5k.png ├── lib ├── kotlin-reflect.jar └── kotlin-runtime.jar ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .travis.yml ├── .gitignore ├── gradle.properties ├── gradlew.bat ├── README.md └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /screenshot/1k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/screenshot/1k.png -------------------------------------------------------------------------------- /screenshot/2k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/screenshot/2k.png -------------------------------------------------------------------------------- /screenshot/3k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/screenshot/3k.png -------------------------------------------------------------------------------- /screenshot/4k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/screenshot/4k.png -------------------------------------------------------------------------------- /screenshot/5k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/screenshot/5k.png -------------------------------------------------------------------------------- /app/src/main/assets/evernote.properties: -------------------------------------------------------------------------------- 1 | consumer_key=itrufeng 2 | consumer_secret=08278d04ea274374 -------------------------------------------------------------------------------- /lib/kotlin-reflect.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/lib/kotlin-reflect.jar -------------------------------------------------------------------------------- /lib/kotlin-runtime.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/lib/kotlin-runtime.jar -------------------------------------------------------------------------------- /app/keystore/release.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/app/keystore/release.jks -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxKotlin/Pocket/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/ClipboardService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service 2 | 3 | interface ClipboardService { 4 | fun content(): String 5 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/IParseDom.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service 2 | 3 | interface IParseDom { 4 | fun getTitle(domContent: String) : String? 5 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/htmlengine/protocol/IHTMLTemplateLoader.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.htmlengine.protocol 2 | 3 | interface IHTMLTemplateLoader { 4 | fun load(path: String): String? 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/model/Tag.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.model 2 | 3 | /** 4 | * Created by jizhang on 1/31/16. 5 | */ 6 | interface Tag { 7 | var name: String? 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/view/IMainView.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.view 2 | 3 | import java.util.* 4 | 5 | interface IMainView { 6 | fun showSaveCloudResult(msg: String) 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/presenter/IMainPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.presenter 2 | 3 | import java.util.* 4 | 5 | interface IMainPresenter { 6 | fun shareWeeklyLinks(fromDate: Date) 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/htmlengine/protocol/IHTMLTemplateRender.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.htmlengine.protocol 2 | 3 | interface IHTMLTemplateRender { 4 | fun render(htmlTemplate: String, data: Map) : String 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/ShareService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service 2 | 3 | import rx.Observable 4 | 5 | interface ShareService { 6 | fun share(title: String, content: String): Observable 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/transform/ILinksToHTML.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.transform 2 | 3 | import com.kotlinchina.smallpockets.model.Link 4 | 5 | interface ILinksToHTML { 6 | fun html(links: List): String? 7 | } 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 21 11:34:03 PDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/model/db/RealmTag.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.model.db 2 | 3 | import io.realm.RealmObject 4 | 5 | /** 6 | * Created by jizhang on 1/31/16. 7 | */ 8 | open class RealmTag: RealmObject() { 9 | open var name: String? = null 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/HttpService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service 2 | 3 | import rx.Observable 4 | 5 | /** 6 | * Created by jizhang on 1/29/16. 7 | */ 8 | interface HttpService { 9 | fun fetchDataWithUrl(url: String): Observable 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/presenter/ILinkListPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.presenter 2 | 3 | interface ILinkListPresenter { 4 | fun checkClipboard() 5 | fun getTitleWithURL(url: String) 6 | fun saveToDB(title: String, url: String, tags: List) 7 | fun refreshList() 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/model/Link.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.model 2 | 3 | import java.util.* 4 | 5 | /** 6 | * Created by jizhang on 1/28/16. 7 | */ 8 | interface Link { 9 | var url: String? 10 | var title: String? 11 | var createDate: Date? 12 | var tags: MutableList? 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 48dp 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/IDataBaseStore.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service 2 | 3 | import com.kotlinchina.smallpockets.model.Link 4 | import java.util.* 5 | 6 | interface IDataBaseStore { 7 | fun saveUrlInfoWithLink(link: Link) 8 | fun loadData(): List 9 | fun queryDataByDate(fromDate: Date, toDate: Date): List 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/model/impl/CoreTag.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.model.impl 2 | 3 | import com.kotlinchina.smallpockets.model.Tag 4 | 5 | /** 6 | * Created by jizhang on 1/31/16. 7 | */ 8 | class CoreTag : Tag { 9 | override var name: String? = null 10 | 11 | constructor(name: String) { 12 | this.name = name 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SmallPockets 3 | Save to Evernote 4 | 需要保存此链接么? 5 | Ok 6 | Cancel 7 | Double Click Exit App 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/view/ILinkListView.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.view 2 | 3 | import com.kotlinchina.smallpockets.model.Link 4 | 5 | interface ILinkListView { 6 | fun showDialog(link: String) 7 | fun showNoLinkWithMsg(msg: String) 8 | fun setSiteListData(data: List) 9 | fun showSaveScreenWithTitle(title: String, url: String) 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/utils/PocketWebViewClient.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.utils 2 | 3 | import android.webkit.WebView 4 | import android.webkit.WebViewClient 5 | 6 | class PocketWebViewClient: WebViewClient() { 7 | override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { 8 | view?.loadUrl(url) 9 | return true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #00000000 8 | #000000 9 | #F06292 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/impl/JxPathParseDom.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service.impl 2 | 3 | import cn.wanghaomiao.xpath.model.JXDocument 4 | import com.kotlinchina.smallpockets.service.IParseDom 5 | 6 | class JxPathParseDom: IParseDom{ 7 | override fun getTitle(domContent: String): String? { 8 | return JXDocument(domContent).sel("//title/text()").first() as? String 9 | } 10 | } -------------------------------------------------------------------------------- /app/src/test/java/com/kotlinchina/smallpockets/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/kotlinchina/smallpockets/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/model/db/RealmLink.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.model.db 2 | 3 | import io.realm.RealmList 4 | import io.realm.RealmObject 5 | import java.util.* 6 | 7 | /** 8 | * Created by jizhang on 1/31/16. 9 | */ 10 | open class RealmLink: RealmObject() { 11 | open var url: String? = null 12 | open var title: String? = null 13 | open var createDate: Date? = null 14 | open var tags: RealmList? = null 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | 3 | jdk: openjdk7 4 | 5 | android: 6 | components: 7 | - build-tools-23.0.1 8 | - android-23 9 | - sys-img-x86-android-23 10 | - extra-google-m2repository 11 | - extra-android-m2repository 12 | 13 | script: ./gradlew test 14 | 15 | deploy: 16 | provider: script 17 | script: ./gradlew uploadReleaseToHockeyApp 18 | on: 19 | branch: master 20 | 21 | 22 | # Slack Notification 23 | notifications: 24 | slack: kotlinchina:dQzkeJ1Fwf9yMsAnTB7zv2yF 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/utils/PocketWebChromeClient.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.utils 2 | 3 | import android.webkit.WebChromeClient 4 | import android.webkit.WebView 5 | 6 | class PocketWebChromeClient: WebChromeClient() { 7 | var titleDidReceived: ((title: String) -> Unit)? = null 8 | override fun onReceivedTitle(view: WebView?, title: String?) { 9 | super.onReceivedTitle(view, title) 10 | if (title == null) { 11 | return 12 | } 13 | titleDidReceived?.invoke(title) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.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 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Log Files 26 | *.log 27 | 28 | # Android Studio Navigation editor temp files 29 | .navigation/ 30 | 31 | # Android Studio captures folder 32 | captures/ 33 | 34 | # IDEA 35 | .idea/ 36 | *.iml 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_basewebview.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/application/PocketApplication.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.application 2 | 3 | import android.app.Application 4 | import io.realm.Realm 5 | import io.realm.RealmConfiguration 6 | 7 | class PocketApplication : Application() { 8 | 9 | override fun onCreate() { 10 | super.onCreate() 11 | setupRealm() 12 | } 13 | 14 | private fun setupRealm() { 15 | val realmConfiguration = RealmConfiguration.Builder(this).build() 16 | Realm.setDefaultConfiguration(realmConfiguration) 17 | } 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/drawable_webview_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/impl/DebugCacheClipboardService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service.impl 2 | 3 | import com.kotlinchina.smallpockets.service.ClipboardService 4 | 5 | class DebugCacheClipboardService: ClipboardService { 6 | override fun content(): String { 7 | val urls = listOf( 8 | "http://pinyin.sogou.com", 9 | "https://jinshuju.net", 10 | "http://www.a-li.com.cn", 11 | "https://realm.io", 12 | "http://zhihu.com", 13 | "http://youku.com" 14 | ) 15 | val random = (Math.random() * urls.count()).toInt() 16 | return urls[random] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/transform/impl/LinksToHTML.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.transform.impl 2 | 3 | import android.content.Context 4 | import com.kotlinchina.htmlengine.HTMLEngineManager 5 | import com.kotlinchina.smallpockets.model.Link 6 | import com.kotlinchina.smallpockets.model.impl.toMap 7 | import com.kotlinchina.smallpockets.transform.ILinksToHTML 8 | 9 | class LinksToHTMLWithHTMLEngine: ILinksToHTML { 10 | private val context: Context 11 | constructor(context: Context) { 12 | this.context = context 13 | } 14 | override fun html(links: List): String? { 15 | val manager = HTMLEngineManager(context, null, null) 16 | return manager.render("www/template.html", links.toMap()) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/model/impl/LinkListMapper.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.model.impl 2 | 3 | import com.kotlinchina.smallpockets.model.Link 4 | import java.text.SimpleDateFormat 5 | 6 | fun List.toMap(): Map { 7 | val simpleDateFormat = SimpleDateFormat("MM-dd") 8 | return mapOf( 9 | "links" to this.map { link -> 10 | mapOf( 11 | "title" to link.title, 12 | "link" to link.url, 13 | "tags" to link.tags?.map { tag -> 14 | mapOf("name" to tag.name) 15 | }, 16 | "date" to simpleDateFormat.format(link.createDate) 17 | ) 18 | } 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/model/impl/CoreLink.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.model.impl 2 | 3 | import com.kotlinchina.smallpockets.model.Link 4 | import com.kotlinchina.smallpockets.model.Tag 5 | import java.util.* 6 | 7 | class CoreLink : Link { 8 | 9 | override var url: String? = null 10 | override var title: String? = null 11 | override var createDate: Date? = null 12 | override var tags: MutableList? = null 13 | 14 | constructor(title: String, url: String, tags: List?) { 15 | this.title = title 16 | this.url = url 17 | this.createDate = Date() 18 | this.tags = ArrayList() 19 | tags?.forEach { 20 | val tag = CoreTag(it) 21 | this.tags?.add(tag) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/htmlengine/impl/HTMLTemplateLoader.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.htmlengine.impl 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import com.kotlinchina.htmlengine.protocol.IHTMLTemplateLoader 6 | import org.apache.commons.io.IOUtils 7 | 8 | class HTMLTemplateLoader: IHTMLTemplateLoader { 9 | private val context: Context 10 | constructor(context: Context) { 11 | this.context = context 12 | } 13 | 14 | override fun load(path: String): String? { 15 | try { 16 | val inputStream = context.assets.open(path) 17 | return IOUtils.toString(inputStream, "UTF-8") 18 | } catch(e: Exception) { 19 | Log.e("${this.javaClass}", "The template can not be load") 20 | } 21 | return null 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/show_site_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 14 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_save_tag.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_web_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | 21 | 22 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /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/chenjunjun/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Realm 20 | -keep class io.realm.annotations.RealmModule 21 | -keep @io.realm.annotations.RealmModule class * 22 | -keep class io.realm.internal.Keep 23 | -keep @io.realm.internal.Keep class * { *; } 24 | -dontwarn javax.** 25 | -dontwarn io.realm.** 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/htmlengine/HTMLEngineManager.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.htmlengine 2 | 3 | import android.content.Context 4 | import com.kotlinchina.htmlengine.impl.HTMLTemplateLoader 5 | import com.kotlinchina.htmlengine.impl.HTMLTemplateRender 6 | import com.kotlinchina.htmlengine.protocol.IHTMLTemplateLoader 7 | import com.kotlinchina.htmlengine.protocol.IHTMLTemplateRender 8 | 9 | class HTMLEngineManager { 10 | private val context: Context 11 | private val loader: T 12 | private val render: U 13 | 14 | constructor(context: Context, loader: T? = null, render: U? = null) { 15 | this.context = context 16 | this.loader = loader ?: HTMLTemplateLoader(context) as T 17 | this.render = render?: HTMLTemplateRender() as U 18 | } 19 | 20 | fun render(path: String, data: Map): String? { 21 | val htmlTemplate = loader.load(path) ?: return null 22 | return render.render(htmlTemplate, data) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/adapter/ShowSiteListAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.adapter 2 | 3 | import android.content.Context 4 | import com.joanzapata.android.BaseAdapterHelper 5 | import com.joanzapata.android.QuickAdapter 6 | import com.kotlinchina.smallpockets.R 7 | import com.kotlinchina.smallpockets.model.Link 8 | import com.kotlinchina.smallpockets.model.Tag 9 | import me.gujun.android.taggroup.TagGroup 10 | 11 | class ShowSiteListAdapter(context: Context, layoutResId: Int, data: List): 12 | QuickAdapter(context, layoutResId, data) { 13 | 14 | override fun convert(helper: BaseAdapterHelper?, item: Link?) { 15 | fun setTag(viewId: Int, tags: List?) { 16 | val tagGroup = helper?.getView(viewId) as TagGroup 17 | val tags = tags?.map { t -> t.name } 18 | tagGroup.setTags(tags) 19 | } 20 | 21 | if (item == null) { return } 22 | helper?.setText(R.id.title, item.title) 23 | setTag(R.id.tag_group, item.tags) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/test/java/com/kotlinchina/smallpockets/service/impl/LinkListMapperKtTest.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service.impl 2 | 3 | import com.kotlinchina.smallpockets.model.Link 4 | import com.kotlinchina.smallpockets.model.impl.CoreLink 5 | import com.kotlinchina.smallpockets.model.impl.toMap 6 | import org.junit.Test 7 | 8 | import org.junit.Assert.* 9 | 10 | class LinkListMapperTest { 11 | @Test 12 | fun testShouldBeMapWhenExecToMap() { 13 | val links = listOf(CoreLink("1", "2", listOf("11", "22")), CoreLink("a", "b", null)) 14 | val map = links.toMap() 15 | val mapedLinks = map["links"] as List> 16 | val mapedLink1Tags = mapedLinks[0]["tags"] as List> 17 | 18 | assertNotNull(mapedLinks) 19 | assertEquals(2, mapedLinks.count()) 20 | assertEquals("1", mapedLinks[0]["title"]) 21 | assertEquals("2", mapedLinks[0]["link"]) 22 | assertEquals("11", mapedLink1Tags[0]["name"]) 23 | assertEquals("22", mapedLink1Tags[1]["name"]) 24 | assertEquals("a", mapedLinks[1]["title"]) 25 | assertEquals("b", mapedLinks[1]["link"]) 26 | assertEquals(emptyList>(), mapedLinks[1]["tags"]) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/impl/WebViewClientHttpService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service.impl 2 | 3 | import android.content.Context 4 | import android.webkit.WebView 5 | import com.kotlinchina.smallpockets.service.HttpService 6 | import com.kotlinchina.smallpockets.utils.PocketWebChromeClient 7 | import com.kotlinchina.smallpockets.utils.PocketWebViewClient 8 | import rx.Observable 9 | import rx.lang.kotlin.observable 10 | 11 | class WebViewClientHttpService: HttpService { 12 | var webview: WebView 13 | val context: Context 14 | val client: PocketWebChromeClient 15 | 16 | constructor(context: Context) { 17 | this.webview = WebView(context) 18 | this.client = PocketWebChromeClient() 19 | this.webview.setWebChromeClient(this.client) 20 | this.webview.setWebViewClient(PocketWebViewClient()) 21 | this.context = context 22 | } 23 | 24 | override fun fetchDataWithUrl(url: String): Observable { 25 | webview.loadUrl(url) 26 | return observable { subscriber -> 27 | client.titleDidReceived = { title -> 28 | subscriber.onNext(title) 29 | subscriber.onCompleted() 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/impl/VolleyHttpService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service.impl 2 | 3 | import android.content.Context 4 | import com.android.volley.RequestQueue 5 | import com.android.volley.toolbox.StringRequest 6 | import com.android.volley.toolbox.Volley 7 | import com.kotlinchina.smallpockets.service.HttpService 8 | import rx.Observable 9 | import rx.lang.kotlin.observable 10 | 11 | class VolleyHttpService constructor(applicationContext: Context): HttpService { 12 | val applicationContext: Context 13 | 14 | init { 15 | this.applicationContext = applicationContext 16 | } 17 | 18 | override fun fetchDataWithUrl(url: String): Observable { 19 | return observable { subscriber -> 20 | val queue: RequestQueue = Volley.newRequestQueue(applicationContext) 21 | val stringRequest = StringRequest(url, { response -> 22 | response.let { 23 | subscriber.onNext(it) 24 | subscriber.onCompleted() 25 | } 26 | }, { error -> 27 | error.let { 28 | subscriber.onError(Exception(error.toString())) 29 | } 30 | }) 31 | queue.add(stringRequest) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/impl/CacheClipboardService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service.impl 2 | 3 | import android.content.ClipData 4 | import android.content.ClipboardManager 5 | import android.content.Context 6 | import com.kotlinchina.smallpockets.BuildConfig 7 | import com.kotlinchina.smallpockets.service.ClipboardService 8 | 9 | class CacheClipboardService: ClipboardService { 10 | private val context: Context 11 | 12 | constructor(context: Context) { 13 | this.context = context 14 | } 15 | 16 | override fun content(): String { 17 | val clipboardManager: ClipboardManager = 18 | context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager 19 | var resultString: String = "" 20 | 21 | if (clipboardManager.hasPrimaryClip()) { 22 | val clipData: ClipData = clipboardManager.primaryClip 23 | val clipDataCount = clipData.itemCount 24 | for (index in 0..clipDataCount - 1) { 25 | val item = clipData.getItemAt(index) 26 | val str = item.coerceToText(context) 27 | resultString += str 28 | } 29 | } 30 | 31 | if (BuildConfig.DEBUG) { 32 | return "http://www.baidu.com" 33 | } else { 34 | return resultString 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/impl/EvernoteShareService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service.impl 2 | 3 | import com.evernote.client.android.EvernoteSession 4 | import com.evernote.client.android.EvernoteUtil 5 | import com.evernote.client.android.asyncclient.EvernoteCallback 6 | import com.evernote.edam.type.Note 7 | import com.kotlinchina.smallpockets.service.ShareService 8 | import rx.Observable 9 | import rx.lang.kotlin.observable 10 | 11 | class EvernoteShareService(evernoteSession: EvernoteSession): ShareService { 12 | private val evernoteSession: EvernoteSession 13 | init { 14 | this.evernoteSession = evernoteSession 15 | } 16 | override fun share(title: String, content: String): Observable { 17 | return observable { 18 | val note = Note() 19 | note.title = title 20 | note.content = EvernoteUtil.NOTE_PREFIX + content + EvernoteUtil.NOTE_SUFFIX 21 | 22 | evernoteSession.evernoteClientFactory?.noteStoreClient?.createNoteAsync(note, object: EvernoteCallback { 23 | override fun onException(error: Exception?) { 24 | it.onError(error) 25 | } 26 | 27 | override fun onSuccess(note: Note?) { 28 | it.onNext("Save success") 29 | it.onCompleted() 30 | } 31 | }) 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/assets/www/template.html: -------------------------------------------------------------------------------- 1 |
2 | @foreach ($links) 3 |
4 |
5 | ${title} 6 |
7 |
8 | 替换此处,描述故事 9 |
10 |
11 |
12 | 标签: 13 |
14 | @foreach ($tags) 15 |
16 | ${name} 17 |
18 | @endforeach 19 |
20 |
21 | 日期: ${date} 22 |
23 |
24 | @endforeach 25 |
26 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/view/Fragment/BaseWebViewFragment.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.view.Fragment 2 | 3 | import android.os.Bundle 4 | import android.support.v4.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import com.kotlinchina.smallpockets.R 9 | import com.kotlinchina.smallpockets.widget.CustomProgressWebView 10 | 11 | /** 12 | * Created by junjun. 13 | */ 14 | class BaseWebViewFragment : Fragment() { 15 | 16 | var customProgressWebView: CustomProgressWebView? = null 17 | 18 | companion object { 19 | fun newInstance(url: String?): BaseWebViewFragment { 20 | val args = Bundle() 21 | args.putString("url", url) 22 | val fragment = BaseWebViewFragment() 23 | fragment.arguments = args 24 | return fragment 25 | } 26 | } 27 | 28 | override fun onActivityCreated(savedInstanceState: Bundle?) { 29 | super.onActivityCreated(savedInstanceState) 30 | val perItemUrl = arguments.get("url") as? String 31 | customProgressWebView?.loadUrl(perItemUrl) 32 | } 33 | 34 | override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { 35 | return inflater?.inflate(R.layout.fragment_basewebview,container,false) 36 | } 37 | 38 | override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { 39 | super.onViewCreated(view, savedInstanceState) 40 | customProgressWebView = view?.findViewById(R.id.customProgressWebView) as? CustomProgressWebView 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/htmlengine/impl/HTMLTemplateRender.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.htmlengine.impl 2 | 3 | import com.kotlinchina.htmlengine.protocol.IHTMLTemplateRender 4 | 5 | class HTMLTemplateRender: IHTMLTemplateRender { 6 | private val beforEachRegex: Regex 7 | private val valueRegex: Regex 8 | constructor() { 9 | this.beforEachRegex = Regex("\\@foreach \\(\\$([\\w]+)\\)([\\w\\W]+)\\@endforeach") 10 | this.valueRegex = Regex("\\$\\{(.+?)\\}") 11 | } 12 | 13 | override fun render(htmlTemplate: String, data: Map): String { 14 | val valueSymbols = valueRegex.findAll(htmlTemplate.toString()) 15 | val subBeforEachSymbols = beforEachRegex.findAll(htmlTemplate.toString()) 16 | 17 | var html = htmlTemplate 18 | subBeforEachSymbols.forEach { subBeforEachSymbol -> 19 | val subKey = subBeforEachSymbol.groupValues[1] 20 | val subhtmlTemplate = subBeforEachSymbol.groupValues[2] 21 | val subDatas = data[subKey] as? List> 22 | var subHtml = "" 23 | subDatas?.forEach { data -> 24 | subHtml += render(subhtmlTemplate, data) 25 | } 26 | html = html.replace(subBeforEachSymbol.groupValues[0], subHtml) 27 | } 28 | valueSymbols.forEach { valueSymbol -> 29 | val valueSymbolKey = valueSymbol.groupValues[1] 30 | val valueSymbolText = valueSymbol.groupValues[0] 31 | val value = data[valueSymbolKey] 32 | html = html.replace(valueSymbolText, value.toString()) 33 | } 34 | return html 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/view/impl/SaveTagDialog.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.view.impl 2 | 3 | import android.app.AlertDialog 4 | import android.app.Dialog 5 | import android.os.Bundle 6 | import android.support.v4.app.DialogFragment 7 | import com.kotlinchina.smallpockets.R 8 | import me.gujun.android.taggroup.TagGroup 9 | 10 | class SaveTagDialog: DialogFragment() { 11 | 12 | companion object { 13 | val TITLE = "title" 14 | val URL = "url" 15 | val TAGS = "tags" 16 | } 17 | 18 | var onSave: ((data: Map)->Unit)? = null 19 | var saveTagGroup: TagGroup? = null 20 | 21 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { 22 | val builder = AlertDialog.Builder(activity) 23 | val title = arguments?.getString(TITLE) 24 | val url = arguments?.getString(URL) 25 | val layoutInflater = activity.layoutInflater 26 | val view = layoutInflater.inflate(R.layout.activity_save_tag, null) 27 | builder.setView(view) 28 | .setTitle(title) 29 | .setMessage(url) 30 | .setPositiveButton("Save", { dialogInterface, i -> 31 | val tags = saveTagGroup?.tags 32 | if (title != null && url != null && tags != null) { 33 | this@SaveTagDialog.onSave?.invoke(mapOf( 34 | TITLE to title, 35 | URL to url, 36 | TAGS to tags.toList() 37 | )) 38 | } 39 | }) 40 | saveTagGroup = view.findViewById(R.id.save_tag_group) as? TagGroup 41 | return builder.create() 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/utils/DoubClickExitHelper.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.utils 2 | 3 | import android.app.Activity 4 | import android.os.Handler 5 | import android.os.Looper 6 | import android.view.KeyEvent 7 | import android.widget.Toast 8 | import com.kotlinchina.smallpockets.R 9 | 10 | /** 11 | * Created by junjun. 12 | */ 13 | class DoubClickExitHelper(private val mActivity: Activity) { 14 | 15 | private var isOnKeyBacking: Boolean? = false 16 | private var mHandler: Handler? = null 17 | private var mBackToast: Toast? = null 18 | 19 | init { 20 | mHandler = Handler(Looper.getMainLooper()) 21 | } 22 | 23 | fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { 24 | if (keyCode != KeyEvent.KEYCODE_BACK) { 25 | return false 26 | } 27 | var isBack = isOnKeyBacking as? Boolean 28 | if (isBack!=null && isBack) { 29 | mHandler?.removeCallbacks(onBackTimeRunnable) 30 | if (mBackToast != null) { 31 | mBackToast?.cancel() 32 | } 33 | // 退出 34 | AppManager.appManager?.AppExit(mActivity) 35 | return true 36 | } else { 37 | isOnKeyBacking = true 38 | if (mBackToast == null) { 39 | mBackToast = Toast.makeText(mActivity, R.string.double_click_exit, 2000) 40 | } 41 | mBackToast?.show() 42 | mHandler?.postDelayed(onBackTimeRunnable, 2000) 43 | return true 44 | } 45 | } 46 | 47 | private val onBackTimeRunnable = Runnable { 48 | isOnKeyBacking = false 49 | if (mBackToast != null) { 50 | mBackToast?.cancel() 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/presenter/impl/MainPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.presenter.impl 2 | 3 | import android.util.Log 4 | import com.kotlinchina.smallpockets.presenter.IMainPresenter 5 | import com.kotlinchina.smallpockets.service.* 6 | import com.kotlinchina.smallpockets.service.impl.CalendarService 7 | import com.kotlinchina.smallpockets.transform.ILinksToHTML 8 | import com.kotlinchina.smallpockets.view.IMainView 9 | import java.text.SimpleDateFormat 10 | import java.util.* 11 | 12 | class MainPresenter: IMainPresenter { 13 | 14 | var mainView: IMainView? = null 15 | var dataBaseStore: IDataBaseStore? = null 16 | var linksToHTML: ILinksToHTML? = null 17 | var shareService: ShareService? = null 18 | 19 | constructor(mainView: IMainView, dataBaseStore: IDataBaseStore, linksToHTML: ILinksToHTML, 20 | shareService: ShareService) { 21 | this.mainView = mainView 22 | this.dataBaseStore = dataBaseStore 23 | this.linksToHTML = linksToHTML 24 | this.shareService = shareService 25 | } 26 | 27 | override fun shareWeeklyLinks(fromDate: Date) { 28 | fun titleForm(beginDate: Date, endDate: Date): String { 29 | val simpleDateFormat = SimpleDateFormat("MM-dd") 30 | return "${simpleDateFormat.format(beginDate)} ~ ${simpleDateFormat.format(endDate)} Weekly" 31 | } 32 | 33 | val datePair = CalendarService().getMondayAndSundayDateOfThisWeek(fromDate) 34 | val title = titleForm(datePair.first, datePair.second) 35 | val links = dataBaseStore?.queryDataByDate(datePair.first, datePair.second) 36 | val html = linksToHTML?.html(links!!) 37 | if (html == null) { 38 | Log.e("${this.javaClass}", "format error") 39 | return 40 | } 41 | 42 | shareService?.share(title, html)?.subscribe ({ 43 | this.mainView?.showSaveCloudResult("Cool") 44 | }, { 45 | this.mainView?.showSaveCloudResult(it.message!!) 46 | }) 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/test/java/com/kotlinchina/smallpockets/presenter/LinkListPresenterTest.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.presenter 2 | 3 | import com.kotlinchina.smallpockets.presenter.impl.LinkListPresenter 4 | import com.kotlinchina.smallpockets.service.* 5 | import com.kotlinchina.smallpockets.transform.ILinksToHTML 6 | import com.kotlinchina.smallpockets.view.ILinkListView 7 | import org.junit.Before 8 | import org.junit.Test 9 | import org.mockito.BDDMockito.* 10 | 11 | class LinkListPresenterTest { 12 | 13 | var presenter: LinkListPresenter? = null 14 | var mockClipboardService: ClipboardService? = null 15 | var mockLinkListActivity: ILinkListView? = null 16 | var mockDatabaseStore: IDataBaseStore? = null 17 | var mockShareService: ShareService? = null 18 | var mockLinksToHTML: ILinksToHTML? = null 19 | 20 | @Before 21 | fun setUp() { 22 | mockLinkListActivity = mock(ILinkListView::class.java) 23 | mockClipboardService = mock(ClipboardService::class.java) 24 | mockDatabaseStore = mock(IDataBaseStore::class.java) 25 | mockShareService = mock(ShareService::class.java) 26 | mockLinksToHTML = mock(ILinksToHTML::class.java) 27 | presenter = LinkListPresenter( 28 | mockLinkListActivity!!, 29 | mock(HttpService::class.java), 30 | mockClipboardService!!, mockDatabaseStore!!) 31 | } 32 | 33 | @Test 34 | fun testCheckClipboardWhenClipBoardHasValidUrl() { 35 | given(mockClipboardService!!.content()).willReturn("http://google.com") 36 | presenter!!.checkClipboard() 37 | then(mockLinkListActivity!!).should(times(1)).showDialog("http://google.com"); 38 | } 39 | 40 | @Test 41 | fun testNotShowLinkWhenCheckClipboardHasInValidUrl() { 42 | given(mockClipboardService!!.content()).willReturn("fjdklsjaklfjdkslajfklasjlk") 43 | presenter!!.checkClipboard() 44 | then(mockLinkListActivity!!).should(times(1)).showNoLinkWithMsg("Invalid String"); 45 | then(mockLinkListActivity!!).should(never()).showDialog(anyString()); 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/impl/RealmStore.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service.impl 2 | 3 | import com.kotlinchina.smallpockets.model.Link 4 | import com.kotlinchina.smallpockets.model.db.RealmLink 5 | import com.kotlinchina.smallpockets.model.db.RealmTag 6 | import com.kotlinchina.smallpockets.model.impl.CoreLink 7 | import com.kotlinchina.smallpockets.service.IDataBaseStore 8 | import io.realm.Realm 9 | import java.util.* 10 | 11 | class RealmStore : IDataBaseStore { 12 | 13 | override fun loadData(): List { 14 | val realm = Realm.getDefaultInstance() 15 | val query = realm.where(RealmLink::class.java) 16 | val queryResults = query.findAll() 17 | 18 | return queryResults.map { link -> 19 | transformRealmLinkToCoreLink(link) 20 | } 21 | } 22 | 23 | override fun saveUrlInfoWithLink(link: Link) { 24 | val realm = Realm.getDefaultInstance() 25 | realm.executeTransaction { 26 | val realmLink = realm.createObject(RealmLink::class.java) 27 | realmLink.title = link.title 28 | realmLink.url = link.url 29 | realmLink.createDate = link.createDate 30 | link.tags?.forEach { it -> 31 | val realmTag = realm.createObject(RealmTag::class.java) 32 | realmTag.name = it.name 33 | realmLink.tags?.add(realmTag) 34 | } 35 | realm.copyFromRealm(realmLink) 36 | } 37 | } 38 | 39 | override fun queryDataByDate(fromDate: Date, toDate: Date): List { 40 | val realm = Realm.getDefaultInstance() 41 | val results = realm.where(RealmLink::class.java).between("createDate", fromDate, toDate).findAll() 42 | return results.map { 43 | transformRealmLinkToCoreLink(it) 44 | } 45 | } 46 | 47 | private fun transformRealmLinkToCoreLink(link: RealmLink): CoreLink { 48 | val title = link.title ?: "" 49 | val url = link.url ?: "" 50 | val tags = link.tags?.map { tag -> 51 | tag.name ?: "" 52 | } 53 | return CoreLink(title, url, tags) 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/test/java/com/kotlinchina/smallpockets/presenter/MainPresenterTest.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.presenter 2 | 3 | import com.kotlinchina.smallpockets.model.Link 4 | import com.kotlinchina.smallpockets.model.impl.CoreLink 5 | import com.kotlinchina.smallpockets.presenter.impl.MainPresenter 6 | import com.kotlinchina.smallpockets.service.IDataBaseStore 7 | import com.kotlinchina.smallpockets.service.ShareService 8 | import com.kotlinchina.smallpockets.transform.ILinksToHTML 9 | import com.kotlinchina.smallpockets.view.IMainView 10 | import org.junit.Before 11 | import org.junit.Test 12 | import org.mockito.BDDMockito.* 13 | import rx.lang.kotlin.observable 14 | import java.text.SimpleDateFormat 15 | 16 | class MainPresenterTest { 17 | var presenter: MainPresenter? = null 18 | var mainView: IMainView? = null 19 | var dataBaseStore: IDataBaseStore? = null 20 | var linksToHTML: ILinksToHTML? = null 21 | var shareService: ShareService? = null 22 | 23 | @Before 24 | fun setUp() { 25 | mainView = mock(IMainView::class.java) 26 | dataBaseStore = mock(IDataBaseStore::class.java) 27 | linksToHTML = mock(ILinksToHTML::class.java) 28 | shareService = mock(ShareService::class.java) 29 | presenter = MainPresenter(mainView!!, dataBaseStore!!, linksToHTML!!, shareService!!) 30 | } 31 | 32 | 33 | @Test 34 | fun testShouldShowSaveCloudResultWhenWeHaveLinkInWeek() { 35 | val today = SimpleDateFormat("yyyy-MM-dd").parse("2016-3-25") 36 | val link1 = CoreLink("", "", null) 37 | link1.createDate = SimpleDateFormat("yyyy-MM-dd").parse("2016-3-24") 38 | val lists = listOf(link1, link1, link1) 39 | val success = observable { 40 | it.onNext("Save Success") 41 | it.onCompleted() 42 | } 43 | given(dataBaseStore!!.queryDataByDate(link1.createDate!!, today)).willReturn(lists) 44 | given(shareService!!.share(anyString(), anyString())).willReturn(success) 45 | given(linksToHTML!!.html(anyListOf(Link::class.java))).willReturn("") 46 | presenter!!.shareWeeklyLinks(today) 47 | then(mainView!!).should(times(1)).showSaveCloudResult("Cool") 48 | } 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/presenter/impl/LinkListPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.presenter.impl 2 | 3 | import com.kotlinchina.smallpockets.model.impl.CoreLink 4 | import com.kotlinchina.smallpockets.presenter.ILinkListPresenter 5 | import com.kotlinchina.smallpockets.service.* 6 | import com.kotlinchina.smallpockets.view.ILinkListView 7 | import java.net.MalformedURLException 8 | import java.net.URL 9 | 10 | class LinkListPresenter(linkListView: ILinkListView, httpService: HttpService, 11 | clipboardService: ClipboardService, 12 | dataBaseStore: IDataBaseStore): ILinkListPresenter { 13 | 14 | var linkListView: ILinkListView 15 | val httpService: HttpService 16 | val clipboardService: ClipboardService 17 | val dataBaseStore: IDataBaseStore 18 | 19 | init { 20 | this.linkListView = linkListView 21 | this.httpService = httpService 22 | this.clipboardService = clipboardService 23 | this.dataBaseStore = dataBaseStore 24 | } 25 | 26 | override fun getTitleWithURL(url: String) { 27 | httpService.fetchDataWithUrl(url) 28 | .subscribe { title -> 29 | if (title != null) linkListView.showSaveScreenWithTitle(title, url) 30 | } 31 | } 32 | 33 | override fun saveToDB(title: String, url: String, tags: List) { 34 | dataBaseStore.saveUrlInfoWithLink(CoreLink(title, url, tags)) 35 | this.linkListView.setSiteListData(dataBaseStore.loadData()) 36 | } 37 | 38 | override fun refreshList() { 39 | this.linkListView.setSiteListData(dataBaseStore.loadData()) 40 | } 41 | 42 | 43 | override fun checkClipboard() { 44 | fun checkClipBoardValidation(clipboardString: String) { 45 | val url = try { 46 | URL(clipboardString) 47 | } catch (e: MalformedURLException) { 48 | this.linkListView.showNoLinkWithMsg("Invalid String") 49 | return 50 | } 51 | 52 | this.linkListView.showDialog("${url.toString()}") 53 | } 54 | checkClipBoardValidation(clipboardService.content()) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/res/layout/link_list_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | 17 | 18 | 28 | 29 | 30 | 31 | 32 | 42 | 43 | 44 | 45 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/service/impl/CalendarService.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.service.impl 2 | 3 | import java.util.* 4 | 5 | class CalendarService { 6 | 7 | fun getMondayAndSundayCalendarOfThisWeek(calendar: Calendar): Pair { 8 | val mondayCalendar = getMondayCalendar(calendar) 9 | val sundayCalendar = getSundayCalendar(calendar) 10 | return Pair(mondayCalendar, sundayCalendar) 11 | } 12 | 13 | fun getMondayAndSundayDateOfThisWeek(calendar: Calendar): Pair { 14 | val datePair = getMondayAndSundayCalendarOfThisWeek(calendar) 15 | return Pair(calendarToDate(datePair.first), calendarToDate(datePair.second)) 16 | } 17 | 18 | fun getMondayAndSundayDateOfThisWeek(date: Date): Pair { 19 | return getMondayAndSundayDateOfThisWeek(dateToCalendar(date)) 20 | } 21 | 22 | private fun getMondayCalendar(calendar: Calendar): Calendar { 23 | var newCalendar = getNewCalendar(calendar) 24 | 25 | val currentDayOfWeek = getDayOfWeek(newCalendar) 26 | newCalendar.add(Calendar.DAY_OF_YEAR, -currentDayOfWeek + 1) 27 | newCalendar.set(Calendar.HOUR, 0) 28 | newCalendar.set(Calendar.MINUTE, 0) 29 | newCalendar.set(Calendar.SECOND, 0) 30 | return newCalendar 31 | } 32 | 33 | private fun getSundayCalendar(calendar: Calendar): Calendar { 34 | val newCalendar = getNewCalendar(calendar) 35 | val currentDayOfWeek = getDayOfWeek(newCalendar) 36 | newCalendar.add(Calendar.DAY_OF_YEAR, 7 - currentDayOfWeek) 37 | newCalendar.set(Calendar.HOUR, 23) 38 | newCalendar.set(Calendar.MINUTE, 59) 39 | newCalendar.set(Calendar.SECOND, 59) 40 | return newCalendar 41 | } 42 | 43 | private fun getDayOfWeek(calendar: Calendar): Int { 44 | val currentDayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1 45 | if (currentDayOfWeek == 0) { 46 | return 7 47 | } else { 48 | return currentDayOfWeek 49 | } 50 | } 51 | 52 | private fun getNewCalendar(calendar: Calendar): Calendar { 53 | var newCalendar = Calendar.getInstance() 54 | newCalendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), 55 | calendar.get(Calendar.DAY_OF_MONTH), 0, 0, 0) 56 | return newCalendar 57 | } 58 | 59 | fun dateToCalendar(date: Date): Calendar { 60 | var calendar = Calendar.getInstance() 61 | calendar.time = date 62 | return calendar 63 | 64 | } 65 | 66 | fun calendarToDate(calendar: Calendar): Date { 67 | return calendar.time 68 | } 69 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![SmallPocket](./app/src/main/res/mipmap-xxxhdpi/ic_launcher.png) 2 | 3 | # SmallPocket 4 | 5 | [![Join the chat at https://gitter.im/RxKotlin/Pocket](https://badges.gitter.im/RxKotlin/Pocket.svg)](https://gitter.im/RxKotlin/Pocket?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | [![Build 7 | Status](https://travis-ci.org/RxKotlin/Pocket.svg?branch=master)](https://travis-ci.org/RxKotlin/Pocket) 8 | 9 | This is a first kotlin app, help user to save links easily, and can export to Evernote as weekly. 10 | 11 | Steps: 12 | 13 | 1. copy link anywhere 14 | 2. open SmallPocket 15 | 3. query save or not 16 | 4. click OK 17 | 5. input tags for this link 18 | 6. click save 19 | 7. click link item show detail 20 | 21 | ![1](./screenshot/1k.png) 22 | ![2](./screenshot/2k.png) 23 | ![3](./screenshot/3k.png) 24 | ![4](./screenshot/4k.png) 25 | ![5](./screenshot/5k.png) 26 | 27 | ## Architectural 28 | 29 | ### Model 30 | |Link|Tag| 31 | |---|---| 32 | |CoreLink|CoreTag| 33 | |RealmLink|RealTag| 34 | 35 | ### View 36 | |IMainView|IListView| 37 | |---|---| 38 | |MainActivity|LinkListFragment| 39 | 40 | ### Presenter 41 | |IMainPresenter|ILinkListPresenter| 42 | |---|---| 43 | |MainPresenter|LinkListPresenter| 44 | 45 | 46 | ## Our Libray writen by Kotlin 47 | 48 | An HTML render engin [HtmlTemplateEngine](https://github.com/RxKotlin/HtmlTemplateEngine) 49 | 50 | ## 3rd Part Libs 51 | 52 | * [RxKotlin](https://github.com/ReactiveX/RxKotlin) 53 | * [Realm](https://realm.io/) 54 | * [Volley](https://github.com/mcxiaoke/android-volley) 55 | * [JsoupXpath](https://github.com/zhegexiaohuozi/JsoupXpath) 56 | * [HockeyApp](https://www.hockeyapp.net/) 57 | * [base-adapter-helper](https://github.com/JoanZapata/base-adapter-helper) 58 | 59 | 60 | ## License 61 | 62 | ``` 63 | The MIT License (MIT) 64 | Copyright (c) 65 | 66 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 67 | 68 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 69 | 70 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 71 | ``` 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/utils/AppManager.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.utils 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | 6 | import java.util.Stack 7 | 8 | @Suppress("UNUSED_VALUE") 9 | /** 10 | * Created by junjun. 11 | */ 12 | class AppManager private constructor() { 13 | 14 | fun addActivity(activity: Activity?) { 15 | if (activityStack == null) { 16 | activityStack = Stack() 17 | } 18 | activityStack?.add(activity) 19 | } 20 | 21 | fun currentActivity(): Activity? { 22 | val activity = activityStack?.lastElement() 23 | return activity as? Activity 24 | } 25 | 26 | fun finishActivity() { 27 | val activity = activityStack?.lastElement() 28 | Companion.finishActivity(activity) 29 | } 30 | 31 | fun finishActivity(cls: Class<*>) { 32 | if(activityStack!=null){ 33 | activityStack?.forEach { activity: Activity -> 34 | if (activity?.javaClass == cls) { 35 | Companion.finishActivity(activity) 36 | } 37 | } 38 | } 39 | 40 | } 41 | 42 | fun finishAllActivity() { 43 | var i = 0 44 | val size = activityStack?.size as Int 45 | while (i < size) { 46 | val tempActivityStack = activityStack as? Stack 47 | if(tempActivityStack!=null && tempActivityStack?.get(i)!=null){ 48 | Companion?.finishActivity(tempActivityStack?.get(i)) 49 | break 50 | i++ 51 | } 52 | 53 | } 54 | activityStack?.clear() 55 | } 56 | 57 | fun AppExit(context: Context) { 58 | try { 59 | finishAllActivity() 60 | android.os.Process.killProcess(android.os.Process.myPid()) 61 | System.exit(0) 62 | } catch (e: Exception) { 63 | } 64 | 65 | } 66 | 67 | companion object { 68 | private var activityStack: Stack? = null 69 | private var instance: AppManager? = null 70 | val appManager: AppManager? 71 | get() { 72 | if (instance == null) { 73 | synchronized (AppManager::class.java) { 74 | if (null == instance) { 75 | instance = AppManager() 76 | } 77 | } 78 | } 79 | return instance as? AppManager 80 | } 81 | 82 | fun getActivity(cls: Class<*>): Activity? { 83 | if (activityStack != null) 84 | activityStack?.forEach { activity:Activity -> 85 | if (activity.javaClass == cls) { 86 | return activity 87 | } 88 | } 89 | return null 90 | } 91 | 92 | fun finishActivity(activity: Activity?) { 93 | val activity = activity 94 | val canRemove = activity?.isFinishing as? Boolean 95 | if (activity != null && canRemove!=null && !canRemove) { 96 | activityStack?.remove(activity) 97 | activity?.finish() 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'de.felixschulze.gradle.hockeyapp' 4 | 5 | def buildID() { 6 | try { 7 | Integer.parseInt("$System.env.TRAVIS_BUILD_NUMBER") 8 | } catch (ignored) { 9 | 1 10 | } 11 | } 12 | 13 | def buildNote() { 14 | def formattedDate = new Date().format('MM/dd/yyyy') 15 | def lastCommitID = 'git rev-parse HEAD'.execute().text.trim() 16 | def lastCommitMsg = 'git log -1 --abbrev-commit'.execute().text.trim() 17 | "**Date**:$formattedDate\n\n" + 18 | "**Build ID**:${buildID()}\n\n" + 19 | "**Last git commit version**:$lastCommitID\n\n" + 20 | "**Last git commit message**:$lastCommitMsg" 21 | } 22 | 23 | android { 24 | compileSdkVersion 23 25 | buildToolsVersion "23.0.1" 26 | 27 | defaultConfig { 28 | applicationId "com.kotlinchina.smallpockets" 29 | minSdkVersion 15 30 | targetSdkVersion 23 31 | versionCode buildID() 32 | versionName "1.0" 33 | // HockeyApp 34 | manifestPlaceholders = [HOCKEYAPP_APP_ID: 'afc9159ea81e4aaaaa37e4cdc4b62b2f'] 35 | } 36 | signingConfigs { 37 | release { 38 | keyAlias 'SmallPocket' 39 | keyPassword 'kotlinchina' 40 | storeFile file('keystore/release.jks') 41 | storePassword 'kotlinchina' 42 | } 43 | } 44 | buildTypes { 45 | release { 46 | minifyEnabled false 47 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 48 | signingConfig signingConfigs.release 49 | } 50 | } 51 | sourceSets { 52 | main.java.srcDirs += 'src/main/kotlin' 53 | } 54 | packagingOptions { 55 | exclude 'META-INF/DEPENDENCIES.txt' 56 | exclude 'META-INF/LICENSE.txt' 57 | exclude 'META-INF/NOTICE.txt' 58 | exclude 'META-INF/NOTICE' 59 | exclude 'META-INF/LICENSE' 60 | exclude 'META-INF/DEPENDENCIES' 61 | exclude 'META-INF/notice.txt' 62 | exclude 'META-INF/license.txt' 63 | exclude 'META-INF/dependencies.txt' 64 | exclude 'META-INF/LGPL2.1' 65 | } 66 | } 67 | 68 | dependencies { 69 | compile fileTree(dir: 'libs', include: ['*.jar']) 70 | testCompile 'junit:junit:4.12' 71 | testCompile "org.robolectric:robolectric:3.0" 72 | testCompile "org.mockito:mockito-core:1.+" 73 | compile 'com.android.support:appcompat-v7:23.1.1' 74 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 75 | 76 | compile 'me.gujun.android.taggroup:library:1.4@aar' 77 | compile 'com.mcxiaoke.volley:library:1.0.19' 78 | compile 'io.reactivex:rxkotlin:0.40.1' 79 | // JsoupXpath 80 | compile 'cn.wanghaomiao:JsoupXpath:0.2.2' 81 | // Realm 82 | compile "io.realm:realm-android-library:${realm_version}@aar" 83 | compile "io.realm:realm-annotations:${realm_version}" 84 | kapt "io.realm:realm-annotations:${realm_version}" 85 | kapt "io.realm:realm-annotations-processor:${realm_version}" 86 | // Adapter 87 | compile 'com.joanzapata.android:base-adapter-helper:1.1.11' 88 | // Evernote 89 | compile 'com.evernote:android-sdk:2.0.0-RC3' 90 | // HockeyApp 91 | compile 'net.hockeyapp.android:HockeySDK:3.7.1' 92 | // Apache commons 93 | compile 'commons-io:commons-io:2.4' 94 | 95 | compile 'com.android.support:design:23.4.0' 96 | 97 | compile 'com.melnykov:floatingactionbutton:1.3.0' 98 | 99 | } 100 | 101 | buildscript { 102 | ext.kotlin_version = '1.0.0' 103 | // Realm 104 | ext.realm_version = '0.87.4' 105 | repositories { 106 | mavenCentral() 107 | } 108 | dependencies { 109 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 110 | } 111 | } 112 | 113 | hockeyapp { 114 | apiToken = "2586d0940aef4d6090e743ff141ee20d" 115 | releaseType = 2 // alpha 116 | notify = 0 117 | status = 2 118 | notesType = 1 119 | notes = buildNote() 120 | } 121 | 122 | repositories { 123 | mavenCentral() 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/view/impl/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.view.impl 2 | 3 | import android.os.Bundle 4 | 5 | import android.support.v7.app.AppCompatActivity 6 | import android.view.KeyEvent 7 | import android.view.Menu 8 | import android.view.MenuItem 9 | import android.widget.Toast 10 | import com.evernote.client.android.EvernoteSession 11 | import com.evernote.client.android.login.EvernoteLoginFragment 12 | import com.kotlinchina.smallpockets.R 13 | import com.kotlinchina.smallpockets.presenter.IMainPresenter 14 | import com.kotlinchina.smallpockets.presenter.impl.MainPresenter 15 | import com.kotlinchina.smallpockets.service.impl.EvernoteShareService 16 | import com.kotlinchina.smallpockets.service.impl.RealmStore 17 | import com.kotlinchina.smallpockets.transform.impl.LinksToHTMLWithHTMLEngine 18 | import com.kotlinchina.smallpockets.utils.AppManager 19 | import com.kotlinchina.smallpockets.utils.DoubClickExitHelper 20 | import com.kotlinchina.smallpockets.view.IMainView 21 | import net.hockeyapp.android.CrashManager 22 | import java.io.IOException 23 | import java.util.* 24 | 25 | class MainActivity : AppCompatActivity(), IMainView, EvernoteLoginFragment.ResultCallback, LinkListFragment.ShareWeeklyLinks { 26 | 27 | companion object { 28 | val SAVE_TAGS = "1000" 29 | val EVERNOTE_SERVICE = EvernoteSession.EvernoteService.PRODUCTION 30 | } 31 | 32 | var mainPresenter: IMainPresenter? = null 33 | var evernoteSession: EvernoteSession? = null 34 | var linkListFragment: LinkListFragment? = null 35 | var doubleClickExit: DoubClickExitHelper? = null 36 | 37 | override fun onCreate(savedInstanceState: Bundle?) { 38 | super.onCreate(savedInstanceState) 39 | setContentView(R.layout.main_activity) 40 | setupFrament() 41 | setupPresneter() 42 | initMember() 43 | } 44 | 45 | private fun initMember() { 46 | AppManager.appManager?.addActivity(this) 47 | doubleClickExit = DoubClickExitHelper(this) 48 | } 49 | 50 | override fun onResume() { 51 | super.onResume() 52 | CrashManager.register(this) 53 | } 54 | 55 | override fun onCreateOptionsMenu(menu: Menu?): Boolean { 56 | menuInflater.inflate(R.menu.menu_main, menu) 57 | return true 58 | } 59 | 60 | override fun onOptionsItemSelected(item: MenuItem?): Boolean { 61 | if (checkEvernoteLogin()) { 62 | mainPresenter?.shareWeeklyLinks(Date()) 63 | } else { 64 | evernoteSession?.authenticate(this) 65 | } 66 | return true 67 | } 68 | 69 | private fun checkEvernoteLogin(): Boolean { 70 | val logined = evernoteSession?.isLoggedIn 71 | return logined ?: false 72 | } 73 | 74 | override fun onLoginFinished(successful: Boolean) { 75 | mainPresenter?.shareWeeklyLinks(Date()) 76 | } 77 | 78 | override fun showSaveCloudResult(msg: String) { 79 | Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() 80 | } 81 | 82 | private fun setupEvernoteSession(): EvernoteSession? { 83 | return try { 84 | val progerties = Properties() 85 | val inStream = assets.open("evernote.properties") 86 | progerties.load(inStream) 87 | val key = progerties.getProperty("consumer_key") 88 | val secret = progerties.getProperty("consumer_secret") 89 | inStream.close() 90 | EvernoteSession.Builder(this) 91 | .setEvernoteService(EVERNOTE_SERVICE) 92 | .setSupportAppLinkedNotebooks(false) 93 | .build(key, secret) 94 | .asSingleton() 95 | } catch (e: IOException) { 96 | null 97 | } 98 | } 99 | 100 | private fun setupPresneter() { 101 | val evernoteSession = setupEvernoteSession() ?: return 102 | mainPresenter = MainPresenter(this, RealmStore(), LinksToHTMLWithHTMLEngine(this), EvernoteShareService(evernoteSession)) 103 | this.evernoteSession = evernoteSession 104 | } 105 | 106 | private fun setupFrament() { 107 | val fragmentTransaction = supportFragmentManager.beginTransaction() 108 | linkListFragment = LinkListFragment() 109 | fragmentTransaction.add(R.id.main_container, linkListFragment) 110 | fragmentTransaction.commit() 111 | (linkListFragment as? LinkListFragment)?.setShareWeeklyLinks(this) 112 | } 113 | 114 | override fun shareLink() { 115 | if (checkEvernoteLogin()) { 116 | mainPresenter?.shareWeeklyLinks(Date()) 117 | } else { 118 | evernoteSession?.authenticate(this) 119 | } 120 | } 121 | 122 | override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { 123 | if(keyCode == KeyEvent.KEYCODE_BACK){ 124 | val backPressedEvent = linkListFragment?.backPressed() as? Boolean 125 | if(backPressedEvent!=null && !backPressedEvent){ 126 | val isExitApp = doubleClickExit?.onKeyDown(keyCode, event) as? Boolean 127 | if(isExitApp!=null){ 128 | return isExitApp 129 | } 130 | } 131 | } 132 | return false ; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/widget/CusTomProgressWebView.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.widget 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.util.AttributeSet 6 | import android.view.KeyEvent 7 | import android.view.View 8 | import android.webkit.JsPromptResult 9 | import android.webkit.JsResult 10 | import android.webkit.WebChromeClient 11 | import android.webkit.WebSettings 12 | import android.webkit.WebView 13 | import android.webkit.WebViewClient 14 | import android.widget.LinearLayout 15 | import android.widget.ProgressBar 16 | /** 17 | * Created by junjun 18 | */ 19 | import com.kotlinchina.smallpockets.R 20 | 21 | class CustomProgressWebView : LinearLayout { 22 | 23 | private var mWebView: WebView? = null 24 | private var mProgressBar: ProgressBar? = null 25 | constructor(context: Context?) : this(context,null){} 26 | constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs,0) {} 27 | constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) { 28 | initView(context) 29 | } 30 | 31 | private fun initView(context: Context?) { 32 | val view = View.inflate(context, R.layout.view_web_progress, this) 33 | mWebView = view?.findViewById(R.id.web_view) as? WebView 34 | mProgressBar = view?.findViewById(R.id.progress_bar) as? ProgressBar 35 | } 36 | 37 | fun loadUrl(url: String?) { 38 | var url = url 39 | if (url == null) { 40 | url = "http://www.baidu.com" 41 | } 42 | initWebview(url) 43 | } 44 | 45 | private fun initWebview(url: String) { 46 | initBaseSetting(url) 47 | setWebViewClient() 48 | setWebchromeClient() 49 | webViewOnkeyListener() 50 | } 51 | 52 | private fun initBaseSetting(url: String) { 53 | mWebView?.addJavascriptInterface(this, "android") 54 | val webSettings = mWebView?.settings 55 | webSettings?.javaScriptEnabled = true 56 | webSettings?.allowFileAccess = true 57 | webSettings?.setSupportZoom(true) 58 | webSettings?.defaultZoom = WebSettings.ZoomDensity.MEDIUM 59 | webSettings?.builtInZoomControls = false 60 | webSettings?.defaultFontSize = 16 61 | mWebView?.loadUrl(url) 62 | } 63 | 64 | private fun setWebViewClient(){ 65 | mWebView?.setWebViewClient(object : WebViewClient() { 66 | override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean { 67 | view?.loadUrl(url) 68 | return true 69 | } 70 | 71 | override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { 72 | mProgressBar?.visibility = View.VISIBLE 73 | super.onPageStarted(view, url, favicon) 74 | } 75 | 76 | override fun onPageFinished(view: WebView?, url: String?) { 77 | mProgressBar?.visibility = View.GONE 78 | super.onPageFinished(view, url) 79 | } 80 | 81 | override fun onLoadResource(view: WebView?, url: String?) { 82 | super.onLoadResource(view, url) 83 | } 84 | 85 | override fun onReceivedError(view: WebView?, errorCode: Int, description: String, failingUrl: String) { 86 | super.onReceivedError(view, errorCode, description, failingUrl) 87 | } 88 | 89 | }) 90 | } 91 | 92 | private fun setWebchromeClient() { 93 | mWebView?.setWebChromeClient(object : WebChromeClient() { 94 | override 95 | fun onJsAlert(view: WebView, url: String, message: String, result: JsResult): Boolean { 96 | return super.onJsAlert(view, url, message, result) 97 | } 98 | 99 | override 100 | fun onJsConfirm(view: WebView, url: String, message: String, result: JsResult): Boolean { 101 | return super.onJsConfirm(view, url, message, result) 102 | } 103 | 104 | override 105 | fun onJsPrompt(view: WebView, url: String, message: String, defaultValue: String, result: JsPromptResult): Boolean { 106 | return super.onJsPrompt(view, url, message, defaultValue, result) 107 | } 108 | 109 | override fun onProgressChanged(view: WebView, newProgress: Int) { 110 | mProgressBar?.progress = newProgress 111 | super.onProgressChanged(view, newProgress) 112 | } 113 | 114 | override fun onReceivedTitle(view: WebView, title: String) { 115 | super.onReceivedTitle(view, title) 116 | } 117 | }) 118 | } 119 | 120 | private fun webViewOnkeyListener() { 121 | mWebView?.setOnKeyListener(View.OnKeyListener { v, keyCode, event -> 122 | if (event.action == KeyEvent.ACTION_DOWN) { 123 | val canGoBack = mWebView?.canGoBack() as? Boolean 124 | if (keyCode == KeyEvent.KEYCODE_BACK && canGoBack!=null && canGoBack) { 125 | mWebView?.goBack() 126 | return@OnKeyListener true 127 | } 128 | } 129 | false 130 | }) 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /app/src/main/java/com/kotlinchina/smallpockets/view/impl/LinkListFragment.kt: -------------------------------------------------------------------------------- 1 | package com.kotlinchina.smallpockets.view.impl 2 | 3 | import android.os.Bundle 4 | import android.support.v4.app.Fragment 5 | import android.support.v4.widget.DrawerLayout 6 | import android.support.v4.widget.SwipeRefreshLayout 7 | import android.support.v7.app.AlertDialog 8 | import android.util.Log 9 | import android.view.LayoutInflater 10 | import android.view.View 11 | import android.view.ViewGroup 12 | import android.widget.ListView 13 | import android.widget.RelativeLayout 14 | import com.kotlinchina.smallpockets.BuildConfig 15 | import com.kotlinchina.smallpockets.R 16 | import com.kotlinchina.smallpockets.adapter.ShowSiteListAdapter 17 | import com.kotlinchina.smallpockets.model.Link 18 | import com.kotlinchina.smallpockets.presenter.ILinkListPresenter 19 | import com.kotlinchina.smallpockets.presenter.impl.LinkListPresenter 20 | import com.kotlinchina.smallpockets.service.impl.CacheClipboardService 21 | import com.kotlinchina.smallpockets.service.impl.DebugCacheClipboardService 22 | import com.kotlinchina.smallpockets.service.impl.RealmStore 23 | import com.kotlinchina.smallpockets.service.impl.WebViewClientHttpService 24 | import com.kotlinchina.smallpockets.view.Fragment.BaseWebViewFragment 25 | import com.kotlinchina.smallpockets.view.ILinkListView 26 | import com.melnykov.fab.FloatingActionButton 27 | import java.util.* 28 | 29 | class LinkListFragment() : Fragment(), ILinkListView ,SwipeRefreshLayout.OnRefreshListener { 30 | 31 | var linkListPresenter: ILinkListPresenter? = null 32 | val CLIPBOARD_TAG: String = "CLIPBOARD" 33 | var listView: ListView? = null 34 | var data: MutableList = ArrayList() 35 | var adapter: ShowSiteListAdapter? = null 36 | var drawerLayout: DrawerLayout? = null 37 | var drawerContent: RelativeLayout? = null 38 | var fab: FloatingActionButton? = null 39 | var shShareWeeklyLinks:ShareWeeklyLinks? = null 40 | var swipeLayout: SwipeRefreshLayout? = null 41 | 42 | override fun onCreate(savedInstanceState: Bundle?) { 43 | super.onCreate(savedInstanceState) 44 | val clipboardService = if (BuildConfig.DEBUG) DebugCacheClipboardService() else CacheClipboardService(activity) 45 | linkListPresenter = LinkListPresenter(linkListView = this, 46 | httpService = WebViewClientHttpService(activity.applicationContext), 47 | clipboardService = clipboardService, 48 | dataBaseStore = RealmStore()) 49 | } 50 | 51 | override fun onActivityCreated(savedInstanceState: Bundle?) { 52 | super.onActivityCreated(savedInstanceState) 53 | linkListPresenter?.refreshList() 54 | linkListPresenter?.checkClipboard() 55 | setupListViewItem() 56 | } 57 | 58 | private fun setupListViewItem() { 59 | listView?.setOnItemClickListener { adapterView, view, i, l -> 60 | val perSaveUrl = data?.get(i)?.url 61 | childFragmentManager.beginTransaction().replace(R.id.drawer_content,BaseWebViewFragment.newInstance(perSaveUrl)).commit() 62 | drawerLayout?.openDrawer(drawerContent) 63 | } 64 | } 65 | 66 | override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { 67 | return inflater?.inflate(R.layout.link_list_fragment, container, false) 68 | } 69 | 70 | override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { 71 | super.onViewCreated(view, savedInstanceState) 72 | initView(view) 73 | setSwipeLayout() 74 | } 75 | 76 | private fun setSwipeLayout() { 77 | swipeLayout?.setOnRefreshListener(this) 78 | swipeLayout?.setSize(SwipeRefreshLayout.LARGE) 79 | swipeLayout?.setColorSchemeResources(android.R.color.holo_red_light, 80 | android.R.color.holo_green_light, android.R.color.holo_orange_light, android.R.color.holo_blue_bright) 81 | } 82 | 83 | private fun initView(view: View?) { 84 | listView = view?.findViewById(R.id.link_list) as? ListView 85 | drawerLayout = view?.findViewById(R.id.drawer_layout) as? DrawerLayout 86 | drawerContent = view?.findViewById(R.id.drawer_content) as? RelativeLayout 87 | fab = view?.findViewById(R.id.fab) as? FloatingActionButton 88 | swipeLayout = view?.findViewById(R.id.swipeLayout) as? SwipeRefreshLayout 89 | var curList = listView as? ListView 90 | if(curList !=null){ 91 | fab?.attachToListView(curList) 92 | } 93 | fab?.setOnClickListener { 94 | shShareWeeklyLinks?.shareLink() 95 | } 96 | } 97 | 98 | override fun showDialog(link: String) { 99 | val dialog: AlertDialog.Builder = AlertDialog.Builder(activity) 100 | dialog.setTitle(R.string.save_link_ornot) 101 | dialog.setMessage(link) 102 | dialog.setPositiveButton(R.string.Ok, { dialogInterface, i -> 103 | linkListPresenter?.getTitleWithURL(link) 104 | }) 105 | dialog.setNegativeButton(R.string.Cancel, { dialogInterface, i -> 106 | Log.d(CLIPBOARD_TAG, "Cancel") 107 | }) 108 | dialog.show() 109 | } 110 | 111 | override fun showNoLinkWithMsg(msg: String) { 112 | Log.e(CLIPBOARD_TAG, msg) 113 | } 114 | 115 | override fun setSiteListData(dataResult: List) { 116 | data?.clear() 117 | data?.addAll(dataResult) 118 | adapter = ShowSiteListAdapter(activity, R.layout.show_site_list_item, dataResult) 119 | listView?.adapter = adapter 120 | swipeLayout?.isRefreshing = false; 121 | } 122 | 123 | override fun showSaveScreenWithTitle(title: String, url: String) { 124 | val dialog = SaveTagDialog() 125 | var bundle = Bundle() 126 | bundle.putString(SaveTagDialog.TITLE, title) 127 | bundle.putString(SaveTagDialog.URL, url) 128 | dialog.arguments = bundle 129 | dialog.onSave = { data -> 130 | val title = data[SaveTagDialog.TITLE] as? String 131 | val url = data[SaveTagDialog.URL] as? String 132 | val tags = data[SaveTagDialog.TAGS] as? List 133 | if (title != null && url != null && tags != null) { 134 | linkListPresenter?.saveToDB(title, url, tags) 135 | } 136 | } 137 | dialog.show(fragmentManager, MainActivity.SAVE_TAGS) 138 | } 139 | 140 | fun backPressed():Boolean { 141 | val isDrawerLayoutOpenOrNot = drawerLayout?.isDrawerOpen(drawerContent) as? Boolean 142 | if(isDrawerLayoutOpenOrNot !=null && isDrawerLayoutOpenOrNot){ 143 | drawerLayout?.closeDrawer(drawerContent) 144 | return true 145 | }else{ 146 | return false 147 | } 148 | } 149 | 150 | fun setShareWeeklyLinks(shShareWeeklyLinks:ShareWeeklyLinks){ 151 | this.shShareWeeklyLinks = shShareWeeklyLinks 152 | } 153 | 154 | interface ShareWeeklyLinks{ 155 | fun shareLink() 156 | } 157 | 158 | override fun onRefresh() { 159 | linkListPresenter?.refreshList() 160 | } 161 | } --------------------------------------------------------------------------------