├── .idea ├── .name ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── dictionaries │ └── Thierry.xml ├── modules.xml ├── runConfigurations.xml ├── compiler.xml ├── gradle.xml └── misc.xml ├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── styles.xml │ │ │ │ └── colors.xml │ │ │ ├── drawable │ │ │ │ ├── launch.png │ │ │ │ ├── icon_tab_stats.png │ │ │ │ ├── icon_tab_project.png │ │ │ │ ├── icon_drawer_account.png │ │ │ │ ├── icon_drawer_issues.png │ │ │ │ ├── icon_drawer_project.png │ │ │ │ ├── icon_drawer_stats.png │ │ │ │ ├── icon_tab_dashboard.png │ │ │ │ ├── icon_tab_settings.png │ │ │ │ └── icon_drawer_dashboard.png │ │ │ ├── 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-w820dp │ │ │ │ └── dimens.xml │ │ │ └── layout │ │ │ │ ├── activity_container.xml │ │ │ │ ├── fragment_container.xml │ │ │ │ ├── activity_launch.xml │ │ │ │ ├── fragment_webview.xml │ │ │ │ ├── fragment_list.xml │ │ │ │ ├── fragment_dashboard.xml │ │ │ │ ├── listitem_issues.xml │ │ │ │ ├── listitem_account.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── listitem_drawer.xml │ │ │ │ ├── listitem_project.xml │ │ │ │ ├── listitem_dashboard.xml │ │ │ │ └── listitem_event.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── thierry │ │ │ │ └── beaconfire │ │ │ │ ├── model │ │ │ │ ├── BaseBean.kt │ │ │ │ ├── TagBean.kt │ │ │ │ ├── SimpleBean.kt │ │ │ │ ├── OrganizationBean.kt │ │ │ │ ├── TeamBean.kt │ │ │ │ ├── ProjectBean.kt │ │ │ │ └── IssueBean.kt │ │ │ │ ├── module │ │ │ │ ├── issues │ │ │ │ │ ├── HistoryViewModel.kt │ │ │ │ │ ├── BookmarksViewModel.kt │ │ │ │ │ ├── AssignedToMeViewModel.kt │ │ │ │ │ ├── HistoryActivity.kt │ │ │ │ │ ├── BookmarksActivity.kt │ │ │ │ │ ├── AssignedToMeActivity.kt │ │ │ │ │ ├── IssuesFragment.kt │ │ │ │ │ └── IssuesViewModel.kt │ │ │ │ ├── project │ │ │ │ │ ├── EventDetailActivity.kt │ │ │ │ │ ├── ProjectListFragment.kt │ │ │ │ │ ├── EventListActivity.kt │ │ │ │ │ ├── ProjectListViewModel.kt │ │ │ │ │ └── EventListViewModel.kt │ │ │ │ ├── account │ │ │ │ │ ├── AccountFragment.kt │ │ │ │ │ └── AccountViewModel.kt │ │ │ │ ├── stats │ │ │ │ │ └── StatsFragment.kt │ │ │ │ ├── LoginActivity.kt │ │ │ │ ├── LaunchActivity.kt │ │ │ │ ├── dashboard │ │ │ │ │ ├── DashboardFragment.kt │ │ │ │ │ └── DashboardViewModel.kt │ │ │ │ ├── common │ │ │ │ │ ├── BaseListAdapter.kt │ │ │ │ │ ├── BaseFragment.kt │ │ │ │ │ ├── BaseActivity.kt │ │ │ │ │ └── BaseListViewModel.kt │ │ │ │ └── MainActivity.kt │ │ │ │ ├── App.kt │ │ │ │ ├── util │ │ │ │ ├── Constants.kt │ │ │ │ └── ButterKnife.kt │ │ │ │ ├── component │ │ │ │ ├── WebViewFragment.kt │ │ │ │ └── ListFragment.kt │ │ │ │ └── service │ │ │ │ └── GMNetService.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── kotlin │ │ │ └── com │ │ │ └── thierry │ │ │ └── beaconfire │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── kotlin │ │ └── com │ │ └── thierry │ │ └── beaconfire │ │ └── ApplicationTest.java ├── proguard-rules.pro ├── build.gradle └── app.iml ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── local.properties ├── gradle.properties ├── Beaconfire.iml ├── README.md ├── gradlew.bat └── gradlew /.idea/.name: -------------------------------------------------------------------------------- 1 | Beaconfire -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Beaconfire 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/launch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/drawable/launch.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_tab_stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/drawable/icon_tab_stats.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_tab_project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/drawable/icon_tab_project.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_drawer_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/drawable/icon_drawer_account.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_drawer_issues.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/drawable/icon_drawer_issues.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_drawer_project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/drawable/icon_drawer_project.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_drawer_stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/drawable/icon_drawer_stats.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_tab_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/drawable/icon_tab_dashboard.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_tab_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/drawable/icon_tab_settings.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_drawer_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thierryxing/sentry-beaconfire/HEAD/app/src/main/res/drawable/icon_drawer_dashboard.png -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/dictionaries/Thierry.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | beaconfire 5 | thierry 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/model/BaseBean.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.model 2 | 3 | import java.io.Serializable 4 | 5 | /** 6 | * Created by Thierry on 16/3/16. 7 | */ 8 | interface BaseBean : Serializable -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/model/TagBean.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.model 2 | 3 | /** 4 | * Created by Thierry on 16/3/9. 5 | */ 6 | data class TagBean(val key: String, 7 | val value: String) : BaseBean -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/model/SimpleBean.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.model 2 | 3 | /** 4 | * Created by Thierry on 16/3/9. 5 | */ 6 | data class SimpleBean(val title: String, 7 | val detail: String) : BaseBean -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri May 19 15:44:02 CST 2017 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-3.3-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/model/OrganizationBean.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.model 2 | 3 | /** 4 | * Created by Thierry on 16/3/9. 5 | */ 6 | data class OrganizationBean(val slug: String, 7 | val dateCreated: String, 8 | val id: String, 9 | val name: String) : BaseBean -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/test/kotlin/com/thierry/beaconfire/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire; 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/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/androidTest/kotlin/com/thierry/beaconfire/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire; 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/thierry/beaconfire/model/TeamBean.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.model 2 | 3 | /** 4 | * Created by Thierry on 16/3/9. 5 | */ 6 | data class TeamBean(var slug: String, 7 | var name: String, 8 | var hasAccess: Boolean, 9 | var isPending: Boolean, 10 | var dataCreated: String, 11 | var isMember: Boolean, 12 | var id: String) : BaseBean -------------------------------------------------------------------------------- /local.properties: -------------------------------------------------------------------------------- 1 | ## This file is automatically generated by Android Studio. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file should *NOT* be checked into Version Control Systems, 5 | # as it contains information specific to your local configuration. 6 | # 7 | # Location of the SDK. This is only used by Gradle. 8 | # For customization when using a Version Control System, please read the 9 | # header note. 10 | sdk.dir=/Users/Thierry/Applications/android-sdk-macosx -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_container.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_container.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_launch.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_webview.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/model/ProjectBean.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.model 2 | 3 | /** 4 | * Created by Thierry on 16/3/9. 5 | */ 6 | data class ProjectBean(val slug: String, 7 | val features: Array, 8 | val isPublic: Boolean, 9 | val isBookmarked: Boolean, 10 | val firstEvent: String, 11 | val team: TeamBean, 12 | val dateCreated: String, 13 | val id: String, 14 | val name: String) : BaseBean -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/issues/HistoryViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.issues 2 | 3 | import com.thierry.beaconfire.module.project.EventListViewModel 4 | import com.thierry.beaconfire.util.Constants 5 | 6 | /** 7 | * Created by Thierry on 16/3/11. 8 | */ 9 | class HistoryViewModel : EventListViewModel("") { 10 | 11 | val per_page = 25 12 | 13 | override fun buildRemoteUrl() { 14 | remoteUrl = Constants.API.History 15 | } 16 | 17 | override fun buildParams() { 18 | params = listOf(Pair("statsPeriod", statsPeriod), Pair("per_page", per_page), Pair("cursor", cursor)) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/issues/BookmarksViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.issues 2 | 3 | import com.thierry.beaconfire.module.project.EventListViewModel 4 | import com.thierry.beaconfire.util.Constants 5 | 6 | /** 7 | * Created by Thierry on 16/3/11. 8 | */ 9 | class BookmarksViewModel : EventListViewModel("") { 10 | 11 | val per_page = 25 12 | 13 | override fun buildRemoteUrl() { 14 | remoteUrl = Constants.API.Bookmarks 15 | } 16 | 17 | override fun buildParams() { 18 | params = listOf(Pair("statsPeriod", statsPeriod), Pair("per_page", per_page), Pair("cursor", cursor)) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/issues/AssignedToMeViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.issues 2 | 3 | import com.thierry.beaconfire.module.project.EventListViewModel 4 | import com.thierry.beaconfire.util.Constants 5 | 6 | /** 7 | * Created by Thierry on 16/3/11. 8 | */ 9 | class AssignedToMeViewModel : EventListViewModel("") { 10 | 11 | val per_page = 25 12 | 13 | override fun buildRemoteUrl() { 14 | remoteUrl = Constants.API.Assigned 15 | } 16 | 17 | override fun buildParams() { 18 | params = listOf(Pair("statsPeriod", statsPeriod), Pair("per_page", per_page), Pair("cursor", cursor)) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #546076 4 | #25a6f7 5 | #FF4081 6 | #FFFFFF 7 | #D6DBE4 8 | #1e263c 9 | #1d87ce 10 | #546076 11 | #6f7e94 12 | #ec5b33 13 | #25a6f7 14 | #f6d846 15 | #546076 16 | 17 | -------------------------------------------------------------------------------- /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/Thierry/Applications/android-sdk-macosx/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/model/IssueBean.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.model 2 | 3 | 4 | /** 5 | * Created by Thierry on 16/3/9. 6 | */ 7 | data class IssueBean(var id: String, 8 | var project: ProjectBean, 9 | var status: String, 10 | var title: String, 11 | var count: Int, 12 | var culprit: String, 13 | var level: String, 14 | var isBookmarked: Boolean, 15 | var isPublic: Boolean, 16 | var userCount: Int, 17 | var firstSeen: String, 18 | var lastSeen: String, 19 | var permalink: String) : BaseBean -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 15 | 16 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/issues/HistoryActivity.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.issues 2 | 3 | import android.os.Bundle 4 | import com.thierry.beaconfire.R 5 | import com.thierry.beaconfire.common.BaseActivity 6 | import com.thierry.beaconfire.component.ListFragment 7 | import com.thierry.beaconfire.module.project.EventListViewModel 8 | 9 | /** 10 | * Created by Thierry on 16/3/16. 11 | */ 12 | class HistoryActivity : BaseActivity() { 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | setContentView(R.layout.activity_container) 17 | this.actionBar.title = "History" 18 | val fragment = ListFragment().setItemLayoutId(R.layout.listitem_event).setViewModel(HistoryViewModel()) 19 | this.replaceFragmentByTag(R.id.fragment_content, fragment, "history") 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/issues/BookmarksActivity.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.issues 2 | 3 | import android.os.Bundle 4 | import com.thierry.beaconfire.R 5 | import com.thierry.beaconfire.common.BaseActivity 6 | import com.thierry.beaconfire.component.ListFragment 7 | import com.thierry.beaconfire.module.project.EventListViewModel 8 | 9 | /** 10 | * Created by Thierry on 16/3/16. 11 | */ 12 | class BookmarksActivity : BaseActivity() { 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | setContentView(R.layout.activity_container) 17 | this.actionBar.title = "Bookmarks" 18 | val fragment = ListFragment().setItemLayoutId(R.layout.listitem_event).setViewModel(BookmarksViewModel()) 19 | this.replaceFragmentByTag(R.id.fragment_content, fragment, "bookmarks") 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/issues/AssignedToMeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.issues 2 | 3 | import android.os.Bundle 4 | import com.thierry.beaconfire.R 5 | import com.thierry.beaconfire.common.BaseActivity 6 | import com.thierry.beaconfire.component.ListFragment 7 | import com.thierry.beaconfire.module.project.EventListViewModel 8 | 9 | /** 10 | * Created by Thierry on 16/3/16. 11 | */ 12 | class AssignedToMeActivity : BaseActivity() { 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | setContentView(R.layout.activity_container) 17 | this.actionBar.title = "Assigned To Me" 18 | val fragment = ListFragment().setItemLayoutId(R.layout.listitem_event).setViewModel(AssignedToMeViewModel()) 19 | this.replaceFragmentByTag(R.id.fragment_content, fragment, "assigned_to_me") 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/project/EventDetailActivity.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.project 2 | 3 | import android.os.Bundle 4 | import com.thierry.beaconfire.R 5 | import com.thierry.beaconfire.common.BaseActivity 6 | import com.thierry.beaconfire.component.WebViewFragment 7 | import com.thierry.beaconfire.util.Constants 8 | 9 | /** 10 | * Created by Thierry on 16/3/16. 11 | */ 12 | /** 13 | * Created by Thierry on 16/3/11. 14 | */ 15 | class EventDetailActivity : BaseActivity() { 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | setContentView(R.layout.activity_container) 20 | actionBar.title = "Event Detail" 21 | val fragment = WebViewFragment(this.intent.getStringExtra("url"), webViewBlock = { _ -> true }) 22 | this.replaceFragmentByTag(R.id.fragment_content, fragment, "event_detail") 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/issues/IssuesFragment.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.issues 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import com.thierry.beaconfire.R 8 | import com.thierry.beaconfire.common.BaseFragment 9 | import com.thierry.beaconfire.component.ListFragment 10 | 11 | /** 12 | * Created by Thierry on 16/3/9. 13 | */ 14 | class IssuesFragment : BaseFragment() { 15 | 16 | override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { 17 | val view = inflater?.inflate(R.layout.fragment_container, container, false) 18 | val fragment = ListFragment().setItemLayoutId(R.layout.listitem_issues).setViewModel(IssuesViewModel()) 19 | this.replaceFragmentByTag(R.id.fragment_content, fragment, "settings") 20 | return view 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /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 | org.gradle.daemon=true -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/account/AccountFragment.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.account 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import com.thierry.beaconfire.R 8 | import com.thierry.beaconfire.common.BaseFragment 9 | import com.thierry.beaconfire.component.ListFragment 10 | 11 | /** 12 | * Created by Thierry on 16/3/26. 13 | */ 14 | class AccountFragment : BaseFragment() { 15 | 16 | override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { 17 | val view = inflater?.inflate(R.layout.fragment_container, container, false) 18 | val fragment = ListFragment().setItemLayoutId(R.layout.listitem_account).setViewModel(AccountViewModel()) 19 | this.replaceFragmentByTag(R.id.fragment_content, fragment, "account") 20 | return view 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/project/ProjectListFragment.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.project 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import com.thierry.beaconfire.R 8 | import com.thierry.beaconfire.common.BaseFragment 9 | import com.thierry.beaconfire.component.ListFragment 10 | 11 | /** 12 | * Created by Thierry on 16/3/9. 13 | */ 14 | class ProjectListFragment : BaseFragment() { 15 | 16 | override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { 17 | val view = inflater?.inflate(R.layout.fragment_container, container, false) 18 | val fragment = ListFragment().setItemLayoutId(R.layout.listitem_project).setViewModel(ProjectListViewModel()) 19 | this.replaceFragmentByTag(R.id.fragment_content, fragment, "projects") 20 | return view 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/stats/StatsFragment.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.stats 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import com.thierry.beaconfire.R 8 | import com.thierry.beaconfire.common.BaseFragment 9 | import com.thierry.beaconfire.component.WebViewFragment 10 | import com.thierry.beaconfire.util.Constants 11 | 12 | /** 13 | * Created by Thierry on 16/3/9. 14 | */ 15 | class StatsFragment : BaseFragment() { 16 | 17 | override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { 18 | val view = inflater?.inflate(R.layout.fragment_container, container, false) 19 | val fragment = WebViewFragment(Constants.Web.Stats, webViewBlock = { _ -> true }) 20 | this.replaceFragmentByTag(R.id.fragment_content, fragment, "stats") 21 | return view 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_dashboard.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 18 | 19 | 20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/project/EventListActivity.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.project 2 | 3 | import android.os.Bundle 4 | import com.thierry.beaconfire.R 5 | import com.thierry.beaconfire.common.BaseActivity 6 | import com.thierry.beaconfire.component.ListFragment 7 | 8 | /** 9 | * Created by Thierry on 16/3/16. 10 | */ 11 | class EventListActivity : BaseActivity() { 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | setContentView(R.layout.activity_container) 16 | val project_slug = intent.getStringExtra("project_slug") 17 | val project_name = intent.getStringExtra("project_name") 18 | this.actionBar.title = project_name 19 | val fragment = ListFragment().setItemLayoutId(R.layout.listitem_event).setViewModel(EventListViewModel(project_slug)) 20 | this.replaceFragmentByTag(R.id.fragment_content, fragment, "event_list") 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /Beaconfire.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/listitem_issues.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | 21 | 22 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/listitem_account.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | 21 | 22 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/App.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire 2 | 3 | import android.app.Application 4 | import android.util.Log 5 | import android.webkit.CookieManager 6 | import android.webkit.ValueCallback 7 | import com.thierry.beaconfire.service.GMNetService 8 | import com.thierry.beaconfire.util.Constants 9 | import kotlin.properties.Delegates 10 | 11 | /** 12 | * Created by Thierry on 16/2/29. 13 | */ 14 | class App : Application() { 15 | 16 | companion object { 17 | var instance: App by Delegates.notNull() 18 | } 19 | 20 | override fun onCreate() { 21 | super.onCreate() 22 | instance = this 23 | initNet() 24 | } 25 | 26 | fun initNet() { 27 | val service: GMNetService = GMNetService.instance 28 | service.apiHost = Constants.Host 29 | } 30 | 31 | fun cookieExist(): Boolean { 32 | val cookie = CookieManager.getInstance().getCookie(Constants.Host) 33 | return (cookie != null && cookie.contains("sentrysid") && cookie.contains("sudo")) 34 | } 35 | 36 | fun cleanCookie() { 37 | CookieManager.getInstance().setCookie(Constants.Host, "") 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/LoginActivity.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module 2 | 3 | import android.os.Bundle 4 | import com.thierry.beaconfire.common.BaseActivity 5 | import com.thierry.beaconfire.component.WebViewFragment 6 | import com.thierry.beaconfire.util.Constants 7 | import org.jetbrains.anko.startActivity 8 | import com.thierry.beaconfire.R 9 | 10 | /** 11 | * Created by Thierry on 16/3/11. 12 | */ 13 | class LoginActivity : BaseActivity() { 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | setContentView(R.layout.activity_container) 18 | val fragment = WebViewFragment(Constants.Web.Login, webViewBlock = { url -> 19 | this.shouldOverrideUrlLoading(url) 20 | }) 21 | this.actionBar.title = "Login" 22 | this.replaceFragmentByTag(R.id.fragment_content, fragment, "login") 23 | } 24 | 25 | fun shouldOverrideUrlLoading(url: String): Boolean { 26 | if (url == Constants.Web.Root) { 27 | startActivity() 28 | finish() 29 | return false 30 | } else { 31 | return true 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | 17 | 18 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/listitem_drawer.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 19 | 20 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/LaunchActivity.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module 2 | 3 | import android.os.Bundle 4 | import com.thierry.beaconfire.App 5 | import com.thierry.beaconfire.R 6 | import com.thierry.beaconfire.common.BaseActivity 7 | import org.jetbrains.anko.doAsync 8 | import org.jetbrains.anko.startActivity 9 | import org.jetbrains.anko.uiThread 10 | 11 | /** 12 | * Created by Thierry on 16/3/11. 13 | */ 14 | class LaunchActivity : BaseActivity() { 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | setContentView(R.layout.activity_launch) 19 | actionBar.hide() 20 | doAsync { 21 | Thread.sleep(2000) 22 | uiThread { 23 | checkLoginStatus() 24 | } 25 | } 26 | } 27 | 28 | fun checkLoginStatus() { 29 | if (App.instance.cookieExist()) { 30 | this.showMainView() 31 | } else { 32 | this.showLoginView() 33 | } 34 | finish() 35 | } 36 | 37 | fun showMainView() { 38 | startActivity() 39 | } 40 | 41 | fun showLoginView() { 42 | App.instance.cleanCookie() 43 | startActivity() 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/util/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.util 2 | 3 | /** 4 | * Created by Thierry on 16/3/9. 5 | */ 6 | object Constants { 7 | 8 | // Default Organization 9 | val CurrentOrganization = "sentry-sc" 10 | 11 | // Default Host 12 | val Host = "https://sentry.io" 13 | 14 | object Web { 15 | val Root = Host + "/" 16 | val Login = Host + "/auth/login/${Constants.CurrentOrganization}/" 17 | val Stats = Host + "/organizations/${Constants.CurrentOrganization}/stats/" 18 | } 19 | 20 | object API { 21 | val Organizations = Host + "/api/0/organizations/" 22 | val Projects = Host + "/api/0/organizations/${Constants.CurrentOrganization}/projects/" 23 | val New = Host + "/api/0/organizations/${Constants.CurrentOrganization}/issues/new/" 24 | val Assigned = Host + "/api/0/organizations/${Constants.CurrentOrganization}/members/me/issues/assigned/" 25 | val Events = Host + "/api/0/projects/${Constants.CurrentOrganization}/%s/issues/" 26 | val Bookmarks = Host + "/api/0/organizations/${Constants.CurrentOrganization}/members/me/issues/bookmarked/" 27 | val History = Host + "/api/0/organizations/${Constants.CurrentOrganization}/members/me/issues/viewed/" 28 | } 29 | 30 | object Broadcast { 31 | val LoginExpired = "LoginExpired" 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/project/ProjectListViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.project 2 | 3 | import android.view.View 4 | import android.widget.AdapterView 5 | import com.github.salomonbrys.kotson.fromJson 6 | import com.google.gson.Gson 7 | import com.thierry.beaconfire.model.ProjectBean 8 | import com.thierry.beaconfire.module.common.BaseListViewModel 9 | import com.thierry.beaconfire.util.Constants 10 | import org.jetbrains.anko.startActivity 11 | 12 | /** 13 | * Created by Thierry on 16/3/11. 14 | */ 15 | class ProjectListViewModel : BaseListViewModel() { 16 | 17 | override fun buildData(dataString: String) { 18 | dataArray = Gson().fromJson>(dataString) 19 | } 20 | 21 | override fun buildRemoteUrl() { 22 | remoteUrl = Constants.API.Projects 23 | } 24 | 25 | override fun buildParams() { 26 | params = listOf() 27 | } 28 | 29 | fun getTitle(position: Int): String { 30 | val project: ProjectBean = dataArray[position] as ProjectBean 31 | return project.name 32 | } 33 | 34 | fun getSlug(position: Int): String { 35 | val project: ProjectBean = dataArray[position] as ProjectBean 36 | return project.slug 37 | } 38 | 39 | override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, item: Long) { 40 | val project: ProjectBean = dataArray[position] as ProjectBean 41 | view?.context?.startActivity("project_slug" to project.slug, "project_name" to project.name) 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/listitem_project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | 21 | 22 | 32 | 33 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/res/layout/listitem_dashboard.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | 21 | 22 | 32 | 33 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/dashboard/DashboardFragment.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.dashboard 2 | 3 | import android.os.Bundle 4 | import android.support.v4.app.FragmentTabHost 5 | import android.util.Log 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import com.thierry.beaconfire.common.BaseFragment 10 | import com.thierry.beaconfire.R 11 | import com.thierry.beaconfire.component.ListFragment 12 | import org.jetbrains.anko.find 13 | 14 | /** 15 | * Created by Thierry on 16/3/9. 16 | */ 17 | class DashboardFragment : BaseFragment() { 18 | 19 | var mTabHost: FragmentTabHost? = null 20 | 21 | override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { 22 | val view = inflater?.inflate(R.layout.fragment_dashboard, container, false) 23 | mTabHost = view?.find(R.id.tab_host) 24 | this.setUpTabs(mTabHost!!) 25 | return view 26 | } 27 | 28 | fun setUpTabs(mTabHost: FragmentTabHost) { 29 | mTabHost.setup(this.activity, childFragmentManager, R.id.tab_content); 30 | this.addTab("Assigned") 31 | this.addTab("New") 32 | } 33 | 34 | fun addTab(type: String) { 35 | var bundle = Bundle() 36 | bundle.putSerializable("ViewModel", DashboardViewModel(type)) 37 | bundle.putInt("ItemLayoutId", R.layout.listitem_dashboard) 38 | mTabHost?.addTab(mTabHost?.newTabSpec(type)?.setIndicator(type), 39 | ListFragment::class.java, bundle) 40 | } 41 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/issues/IssuesViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.issues 2 | 3 | import android.view.View 4 | import android.widget.AdapterView 5 | import com.thierry.beaconfire.model.SimpleBean 6 | import com.thierry.beaconfire.module.common.BaseListViewModel 7 | import com.thierry.beaconfire.module.issues.AssignedToMeActivity 8 | import com.thierry.beaconfire.module.issues.BookmarksActivity 9 | import com.thierry.beaconfire.module.issues.HistoryActivity 10 | import org.jetbrains.anko.startActivity 11 | 12 | /** 13 | * Created by Thierry on 16/3/11. 14 | */ 15 | class IssuesViewModel : BaseListViewModel() { 16 | 17 | override fun fetchRemoteData() { 18 | dataArray = listOf(SimpleBean("Assigned To Me", ""), SimpleBean("Bookmarks", ""), SimpleBean("History", "")) 19 | fetchDataResult.set(FetchDataResult.Success) 20 | } 21 | 22 | override fun buildData(dataString: String) { 23 | } 24 | 25 | override fun buildRemoteUrl() { 26 | remoteUrl = "" 27 | } 28 | 29 | override fun buildParams() { 30 | params = listOf() 31 | } 32 | 33 | fun getTitle(position: Int): String { 34 | val settings: SimpleBean = dataArray[position] as SimpleBean 35 | return settings.title 36 | } 37 | 38 | override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, item: Long) { 39 | if (position == 0) { 40 | view?.context?.startActivity() 41 | } else if (position == 1) { 42 | view?.context?.startActivity() 43 | } else if (position == 2) { 44 | view?.context?.startActivity() 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/common/BaseListAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.common 2 | 3 | import android.content.Context 4 | import android.databinding.DataBindingUtil 5 | import android.databinding.ViewDataBinding 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.widget.BaseAdapter 10 | import com.thierry.beaconfire.BR 11 | import com.thierry.beaconfire.component.ListFragment 12 | 13 | /** 14 | * Created by Thierry on 16/3/12. 15 | */ 16 | class BaseListAdapter(val fragment: ListFragment, val viewModel: BaseListViewModel, val layoutId: Int) : BaseAdapter() { 17 | 18 | var inflater: LayoutInflater 19 | 20 | init { 21 | inflater = LayoutInflater.from(fragment.activity) 22 | } 23 | 24 | override fun getCount(): Int { 25 | return viewModel.dataArray.count() 26 | } 27 | 28 | override fun getItem(position: Int): Any? { 29 | return viewModel.dataArray[position] 30 | } 31 | 32 | override fun getItemId(position: Int): Long { 33 | return position.toLong() 34 | } 35 | 36 | override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? { 37 | var mConvertView = convertView 38 | var binding: ViewDataBinding? 39 | if (mConvertView == null) { 40 | binding = DataBindingUtil.inflate(inflater, layoutId, parent, false) 41 | mConvertView = binding?.root 42 | mConvertView?.tag = binding 43 | } else { 44 | binding = mConvertView.tag as ViewDataBinding 45 | } 46 | binding?.setVariable(BR.viewModel, viewModel) 47 | binding?.setVariable(BR.position, position) 48 | return mConvertView 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/common/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.common 2 | 3 | import android.app.ProgressDialog 4 | import android.content.Intent 5 | import android.support.annotation.IdRes 6 | import android.support.v4.app.Fragment 7 | import android.support.v4.content.LocalBroadcastManager 8 | import android.util.Log 9 | import android.widget.Toast 10 | import com.thierry.beaconfire.util.Constants 11 | 12 | /** 13 | * Created by Thierry on 16/2/19. 14 | */ 15 | abstract class BaseFragment : Fragment() { 16 | 17 | val TAG = this.javaClass.canonicalName 18 | var dialog: ProgressDialog? = null 19 | 20 | fun showLoading() { 21 | Log.d(TAG, "showLoading") 22 | if (activity != null) { 23 | dialog = ProgressDialog.show(activity, null, "Loading...") 24 | } 25 | } 26 | 27 | fun hideLoading() { 28 | try { 29 | if (dialog != null && dialog!!.isShowing) { 30 | dialog?.dismiss() 31 | } 32 | } catch(e: Exception) { 33 | } 34 | } 35 | 36 | fun toastShow(message: String, duration: Int = Toast.LENGTH_LONG) { 37 | Toast.makeText(activity, message, duration).show() 38 | } 39 | 40 | /** 41 | * replaceFragment 42 | 43 | * @param layoutId 44 | * * 45 | * @param fragment 46 | * * 47 | * @param tag 48 | */ 49 | fun replaceFragmentByTag(@IdRes layoutId: Int, fragment: Fragment, tag: String) { 50 | val fragmentManager = childFragmentManager 51 | val transaction = fragmentManager.beginTransaction() 52 | transaction.replace(layoutId, fragment, tag) 53 | transaction.commitAllowingStateLoss() 54 | fragmentManager.executePendingTransactions() 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/common/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.common 2 | 3 | import android.app.ProgressDialog 4 | import android.os.Bundle 5 | import android.support.annotation.IdRes 6 | import android.support.v4.app.Fragment 7 | import android.support.v4.app.FragmentActivity 8 | import android.util.Log 9 | import android.widget.Toast 10 | 11 | /** 12 | * Created by Thierry on 16/2/19. 13 | */ 14 | abstract class BaseActivity : FragmentActivity() { 15 | 16 | val TAG = this.javaClass.canonicalName 17 | var dialog: ProgressDialog? = null 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | } 22 | 23 | fun showLoading() { 24 | dialog = ProgressDialog.show(this, null, "Loading...") 25 | } 26 | 27 | fun hideLoading() { 28 | try { 29 | if (dialog != null && dialog!!.isShowing) { 30 | dialog?.dismiss() 31 | } 32 | } catch(e: Exception) { 33 | } 34 | } 35 | 36 | fun toastShow(message: String, duration: Int = Toast.LENGTH_LONG) { 37 | Toast.makeText(this, message, duration).show() 38 | } 39 | 40 | /** 41 | * replaceFragment 42 | 43 | * @param layoutId 44 | * * 45 | * @param fragment 46 | * * 47 | * @param tag 48 | */ 49 | fun replaceFragmentByTag(@IdRes layoutId: Int, fragment: Fragment, tag: String) { 50 | Log.d(TAG, "tag" + tag) 51 | val fragmentManager = supportFragmentManager 52 | val transaction = fragmentManager.beginTransaction() 53 | transaction.replace(layoutId, fragment, tag) 54 | transaction.commitAllowingStateLoss() 55 | fragmentManager.executePendingTransactions() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sentry Beaconfire 2 | Kotlin based getsentry/sentry android client, now you can care about your server's error logging and aggregation over the android phone. 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ## About Sentry 13 | [Sentry](https://github.com/getsentry/sentry) is a modern error logging and aggregation platform. 14 | 15 | ## About Project 16 | * Using Kotlin with MVVM pattern. 17 | * Using Android Databinding framework. 18 | 19 | ## Feature List 20 | * Configure your own sentry server host and organization. 21 | * Login to your own sentry server. 22 | * Never miss any Assigned and New issues with Dashboard. 23 | * All projects and teams listed. 24 | * Track issues in a project and see more detail. 25 | 26 | ## How to use 27 | Simply replace the defaults with your own sentry server host and organization in Constants.kt 28 | ```kotlin 29 | // Default Organization 30 | val CurrentOrganization = "YOUR_ORGANIZATION" 31 | 32 | // Default Host 33 | val Host = "YOUR_SENTRY_HOST" 34 | ``` 35 | ## Sentry Version 36 | As far as I know it worked well with Sentry 8.0+, but it may compatible with lower versions, so if anyone find that please let me know, thank you! 37 | 38 | ## Contribute 39 | It is just the beginning of Beaconfire, so anyone interested, feel free to fork it and pull requests to me. Let's make it more fun. 40 | 41 | ## Author 42 | Thierry Xing thierry.xing@gmail.com 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/account/AccountViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.account 2 | 3 | import android.content.Intent 4 | import android.support.v4.content.LocalBroadcastManager 5 | import android.view.View 6 | import android.widget.AdapterView 7 | import com.thierry.beaconfire.App 8 | import com.thierry.beaconfire.common.BaseActivity 9 | import com.thierry.beaconfire.model.IssueBean 10 | import com.thierry.beaconfire.model.SimpleBean 11 | import com.thierry.beaconfire.module.common.BaseListViewModel 12 | import com.thierry.beaconfire.module.issues.AssignedToMeActivity 13 | import com.thierry.beaconfire.module.issues.BookmarksActivity 14 | import com.thierry.beaconfire.util.Constants 15 | import org.jetbrains.anko.startActivity 16 | 17 | /** 18 | * Created by Thierry on 16/3/11. 19 | */ 20 | class AccountViewModel : BaseListViewModel() { 21 | 22 | val localBroadcastManager = LocalBroadcastManager.getInstance(App.instance) 23 | 24 | override fun fetchRemoteData() { 25 | dataArray = listOf(SimpleBean("Sign Out", "")) 26 | fetchDataResult.set(FetchDataResult.Success) 27 | } 28 | 29 | override fun buildData(dataString: String) { 30 | } 31 | 32 | override fun buildRemoteUrl() { 33 | remoteUrl = "" 34 | } 35 | 36 | override fun buildParams() { 37 | params = listOf() 38 | } 39 | 40 | fun getTitle(position: Int): String { 41 | val settings: SimpleBean = dataArray[position] as SimpleBean 42 | return settings.title 43 | } 44 | 45 | override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, item: Long) { 46 | if (position == 0) { 47 | val intent: Intent = Intent(); 48 | intent.action = Constants.Broadcast.LoginExpired 49 | localBroadcastManager.sendBroadcast(intent) 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.1.2' 3 | 4 | repositories { 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 10 | } 11 | } 12 | 13 | apply plugin: 'com.android.application' 14 | apply plugin: 'kotlin-android' 15 | apply plugin: 'kotlin-kapt' 16 | 17 | android { 18 | dataBinding { 19 | enabled = true 20 | } 21 | 22 | compileSdkVersion 23 23 | buildToolsVersion '25.0.0' 24 | 25 | defaultConfig { 26 | applicationId "com.thierry.beaconfire" 27 | minSdkVersion 16 28 | targetSdkVersion 23 29 | versionCode 11 30 | versionName "1.1" 31 | } 32 | buildTypes { 33 | release { 34 | minifyEnabled false 35 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 36 | } 37 | } 38 | sourceSets { 39 | main.java.srcDirs += 'src/main/java' 40 | } 41 | } 42 | 43 | dependencies { 44 | compile fileTree(dir: 'libs', include: ['*.jar']) 45 | testCompile 'junit:junit:4.12' 46 | compile 'com.android.support:appcompat-v7:23.2.1' 47 | compile 'com.android.support:recyclerview-v7:23.2.1' 48 | compile 'com.jakewharton:butterknife:7.0.1' 49 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 50 | compile 'org.jetbrains.anko:anko-sdk15:0.9' // sdk19, sdk21, sdk23 are also available 51 | compile 'org.jetbrains.anko:anko-support-v4:0.9' // In case you need support-v4 bindings 52 | compile 'org.jetbrains.anko:anko-appcompat-v7:0.9' // For appcompat-v7 bindings 53 | compile 'com.github.kittinunf.fuel:fuel-android:1.0.0' //http framework 54 | compile 'com.github.salomonbrys.kotson:kotson:2.1.0' //kotlin lib for GSON 55 | compile 'com.balysv.materialmenu:material-menu-toolbar:1.5.4' 56 | compile 'com.balysv.materialmenu:material-menu:1.5.4' 57 | kapt 'com.android.databinding:compiler:2.3.0' 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/dashboard/DashboardViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.dashboard 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import android.view.View 6 | import android.view.ViewParent 7 | import android.widget.AdapterView 8 | import android.widget.ListView 9 | import com.github.salomonbrys.kotson.fromJson 10 | import com.google.gson.Gson 11 | import com.thierry.beaconfire.R 12 | import com.thierry.beaconfire.model.IssueBean 13 | import com.thierry.beaconfire.module.common.BaseListAdapter 14 | import com.thierry.beaconfire.module.common.BaseListViewModel 15 | import com.thierry.beaconfire.module.project.EventDetailActivity 16 | import com.thierry.beaconfire.util.Constants 17 | import org.jetbrains.anko.startActivity 18 | 19 | /** 20 | * Created by Thierry on 16/3/11. 21 | */ 22 | class DashboardViewModel(val type: String = "Assigned") : BaseListViewModel() { 23 | 24 | val statsPeriod = "24h" 25 | val per_page = 5 26 | val status = "unresolved" 27 | 28 | 29 | override fun buildData(dataString: String) { 30 | dataArray = Gson().fromJson>(dataString) 31 | } 32 | 33 | override fun buildRemoteUrl() { 34 | if (type == "Assigned") { 35 | remoteUrl = Constants.API.Assigned 36 | } else { 37 | remoteUrl = Constants.API.New 38 | } 39 | } 40 | 41 | override fun buildParams() { 42 | params = listOf(Pair("cursor", cursor), Pair("statsPeriod", statsPeriod), Pair("per_page", per_page), Pair("status", status)) 43 | } 44 | 45 | fun getName(position: Int): String { 46 | val issue: IssueBean = dataArray[position] as IssueBean 47 | return issue.title 48 | } 49 | 50 | fun getDetail(position: Int): String { 51 | val issue: IssueBean = dataArray[position] as IssueBean 52 | return "[${issue.project.name})] ${issue.culprit}" 53 | } 54 | 55 | override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, item: Long) { 56 | val issue: IssueBean = dataArray[position] as IssueBean 57 | view?.context?.startActivity("url" to issue.permalink) 58 | } 59 | 60 | 61 | } -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/project/EventListViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.project 2 | 3 | import android.view.View 4 | import android.widget.AdapterView 5 | import com.github.salomonbrys.kotson.fromJson 6 | import com.google.gson.Gson 7 | import com.thierry.beaconfire.R 8 | import com.thierry.beaconfire.model.IssueBean 9 | import com.thierry.beaconfire.module.common.BaseListViewModel 10 | import com.thierry.beaconfire.util.Constants 11 | import org.jetbrains.anko.startActivity 12 | 13 | /** 14 | * Created by Thierry on 16/3/11. 15 | */ 16 | open class EventListViewModel(val project_slug: String) : BaseListViewModel() { 17 | 18 | val statsPeriod = "24h" 19 | val query = "is:unresolved" 20 | val limit = 25 21 | 22 | override fun buildData(dataString: String) { 23 | dataArray = Gson().fromJson>(dataString) 24 | } 25 | 26 | override fun buildRemoteUrl() { 27 | remoteUrl = Constants.API.Events.format(project_slug) 28 | } 29 | 30 | override fun buildParams() { 31 | params = listOf(Pair("statsPeriod", statsPeriod), Pair("query", query), Pair("limit", limit), Pair("cursor", cursor)) 32 | } 33 | 34 | fun getTitle(position: Int): String { 35 | val issue: IssueBean = dataArray[position] as IssueBean 36 | return issue.culprit 37 | } 38 | 39 | fun getDetail(position: Int): String { 40 | val issue: IssueBean = dataArray[position] as IssueBean 41 | return issue.title 42 | } 43 | 44 | fun getFlag(position: Int): String { 45 | val issue: IssueBean = dataArray[position] as IssueBean 46 | return "Events:${issue.count} Users:${(issue.userCount)}" 47 | } 48 | 49 | fun getLevelColor(position: Int): Int { 50 | val issue: IssueBean = dataArray[position] as IssueBean 51 | if (issue.level == "error") { 52 | return R.color.LevelError 53 | } else if (issue.level == "info") { 54 | return R.color.LevelInfo 55 | } else if (issue.level == "warning") { 56 | return R.color.LevelWarning 57 | } else { 58 | return R.color.LevelDefault 59 | } 60 | } 61 | 62 | override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, item: Long) { 63 | val issue: IssueBean = dataArray[position] as IssueBean 64 | view?.context?.startActivity("url" to issue.permalink) 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/common/BaseListViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module.common 2 | 3 | import android.databinding.BaseObservable 4 | import android.databinding.ObservableField 5 | import android.util.Log 6 | import android.view.View 7 | import android.widget.AdapterView 8 | import com.google.gson.Gson 9 | import com.thierry.beaconfire.model.IssueBean 10 | import com.thierry.beaconfire.module.project.EventDetailActivity 11 | import com.thierry.beaconfire.service.GMNetService 12 | import com.thierry.beaconfire.service.HttpMethod 13 | import org.jetbrains.anko.startActivity 14 | import java.io.Serializable 15 | import java.text.FieldPosition 16 | 17 | /** 18 | * Created by Thierry on 16/3/11. 19 | */ 20 | 21 | abstract class BaseListViewModel : BaseObservable(), Serializable, AdapterView.OnItemClickListener { 22 | 23 | val TAG = this.javaClass.canonicalName 24 | var remoteUrl = "" 25 | var message = "" 26 | var cursor = "" 27 | var params: List>? = null 28 | var fetchDataResult: ObservableField = ObservableField() 29 | open var dataArray: List = mutableListOf() 30 | 31 | enum class FetchDataResult { 32 | Normal, 33 | Success, 34 | Failed 35 | } 36 | 37 | open fun fetchRemoteData() { 38 | this.buildRemoteUrl() 39 | this.buildParams() 40 | fetchDataResult.set(FetchDataResult.Normal) 41 | GMNetService.instance.doRequest(remoteUrl, HttpMethod.HttpMethodGet, params, { response -> 42 | Log.d(TAG, "remoteUrl" + remoteUrl) 43 | if (remoteUrl == "") { 44 | fetchDataResult.set(FetchDataResult.Failed) 45 | } 46 | try { 47 | val jsonString: String = String(response.data) 48 | buildData(jsonString) 49 | fetchDataResult.set(FetchDataResult.Success) 50 | } catch (e: Exception) { 51 | fetchDataResult.set(FetchDataResult.Failed) 52 | message = "Fetch Data Error" 53 | } 54 | }, { error -> 55 | fetchDataResult.set(FetchDataResult.Failed) 56 | message = error 57 | }) 58 | } 59 | 60 | abstract fun buildData(dataString: String) 61 | 62 | abstract fun buildRemoteUrl() 63 | 64 | abstract fun buildParams() 65 | 66 | override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, item: Long) { 67 | } 68 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 30 | 31 | 34 | 35 | 38 | 39 | 42 | 43 | 46 | 47 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /app/src/main/res/layout/listitem_event.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | 22 | 23 | 30 | 31 | 42 | 43 | 53 | 54 | 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/component/WebViewFragment.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.component 2 | 3 | import android.graphics.Bitmap 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.webkit.WebSettings 9 | import android.webkit.WebView 10 | import android.webkit.WebViewClient 11 | import com.thierry.beaconfire.R 12 | import com.thierry.beaconfire.common.BaseFragment 13 | import org.jetbrains.anko.* 14 | 15 | /** 16 | * Created by Thierry on 16/3/10. 17 | */ 18 | 19 | open class WebViewFragment(val url: String, val webViewBlock: (url: String) -> Boolean?) : BaseFragment() { 20 | 21 | override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { 22 | val view = inflater?.inflate(R.layout.fragment_webview, container, false) 23 | val webView: WebView = view!!.find(R.id.web_view) 24 | val webSettings: WebSettings = webView.settings 25 | webSettings.javaScriptEnabled = true 26 | webView.setWebViewClient(WebViewClientBase(this)) 27 | webView.loadUrl(url) 28 | return view 29 | } 30 | 31 | fun shouldOverrideUrlLoading(url: String): Boolean { 32 | if (webViewBlock != null) { 33 | return webViewBlock(url)!! 34 | } else { 35 | return true 36 | } 37 | } 38 | 39 | private class WebViewClientBase(var context: WebViewFragment) : WebViewClient() { 40 | 41 | override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { 42 | return context.shouldOverrideUrlLoading(url) 43 | } 44 | 45 | 46 | override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { 47 | super.onPageStarted(view, url, favicon); 48 | context.showLoading() 49 | } 50 | 51 | override fun onPageFinished(view: WebView, url: String) { 52 | super.onPageFinished(view, url) 53 | context.hideLoading() 54 | } 55 | 56 | override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) { 57 | super.onReceivedError(view, errorCode, description, failingUrl); 58 | context.hideLoading() 59 | context.toastShow(description) 60 | } 61 | 62 | 63 | override fun doUpdateVisitedHistory(view: WebView, url: String, isReload: Boolean) { 64 | super.doUpdateVisitedHistory(view, url, isReload); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/component/ListFragment.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.component 2 | 3 | import android.databinding.DataBindingUtil 4 | import android.databinding.Observable 5 | import android.os.Bundle 6 | import android.util.Log 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import android.widget.ListView 11 | import com.thierry.beaconfire.R 12 | import com.thierry.beaconfire.common.BaseFragment 13 | import com.thierry.beaconfire.databinding.FragmentListBinding 14 | import com.thierry.beaconfire.module.common.BaseListAdapter 15 | import com.thierry.beaconfire.module.common.BaseListViewModel 16 | import com.thierry.beaconfire.module.common.BaseListViewModel.FetchDataResult 17 | import org.jetbrains.anko.find 18 | import org.jetbrains.anko.support.v4.toast 19 | 20 | /** 21 | * Created by Thierry on 16/3/11. 22 | */ 23 | 24 | class ListFragment() : BaseFragment() { 25 | 26 | var listView: ListView? = null 27 | var viewModel: BaseListViewModel? = null 28 | var itemLayoutId: Int? = null 29 | 30 | override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { 31 | this.initParams() 32 | val view = initBinding(inflater, container) 33 | this.addKVO() 34 | this.fetchData() 35 | return view 36 | } 37 | 38 | fun setViewModel(viewModel: BaseListViewModel): ListFragment { 39 | this.viewModel = viewModel 40 | return this 41 | } 42 | 43 | fun setItemLayoutId(itemLayoutId: Int): ListFragment { 44 | this.itemLayoutId = itemLayoutId 45 | return this 46 | } 47 | 48 | fun initParams() { 49 | if (this.arguments != null) { 50 | viewModel = this.arguments.getSerializable("ViewModel") as BaseListViewModel 51 | itemLayoutId = this.arguments.getInt("ItemLayoutId") 52 | } 53 | } 54 | 55 | fun initBinding(inflater: LayoutInflater?, container: ViewGroup?): View { 56 | val mBinding: FragmentListBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_list, container, false) 57 | val view = mBinding.root 58 | listView = view.find(R.id.list_view) 59 | mBinding.viewModel = viewModel 60 | return view 61 | } 62 | 63 | fun fetchData() { 64 | showLoading() 65 | viewModel?.fetchRemoteData() 66 | } 67 | 68 | fun addKVO() { 69 | viewModel?.fetchDataResult?.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() { 70 | override fun onPropertyChanged(sender: Observable, propertyId: Int) { 71 | this@ListFragment.observeHandler(viewModel!!.fetchDataResult.get()) 72 | } 73 | }) 74 | } 75 | 76 | fun observeHandler(newValue: FetchDataResult) { 77 | if (newValue == FetchDataResult.Success) { 78 | hideLoading() 79 | if (viewModel!!.dataArray.count() > 0) { 80 | listView?.adapter = BaseListAdapter(this, viewModel!!, itemLayoutId!!) 81 | } else { 82 | toastShow("Nothing to show here, move along") 83 | } 84 | } else if (newValue == FetchDataResult.Failed) { 85 | hideLoading() 86 | toastShow("Get data failed") 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/service/GMNetService.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.service 2 | 3 | import android.content.Intent 4 | import android.support.v4.content.LocalBroadcastManager 5 | import android.util.Log 6 | import android.webkit.CookieManager 7 | import com.github.kittinunf.fuel.android.extension.responseJson 8 | import com.github.kittinunf.fuel.core.Response 9 | import com.github.kittinunf.fuel.httpDelete 10 | import com.github.kittinunf.fuel.httpGet 11 | import com.github.kittinunf.fuel.httpPost 12 | import com.github.kittinunf.fuel.httpPut 13 | import com.github.kittinunf.fuel.core.Manager 14 | import com.thierry.beaconfire.App 15 | import com.thierry.beaconfire.util.Constants 16 | import org.jetbrains.anko.custom.async 17 | import org.jetbrains.anko.uiThread 18 | 19 | /** @brief Http状态枚举 */ 20 | enum class HttpStatusCode(val code: Int) { 21 | HttpStatusCodeSuccess(200), 22 | HttpStatusCodeError(500), 23 | HttpStatusCodeUnauthorized(403) 24 | }; 25 | 26 | /** @brief Http方法枚举 */ 27 | enum class HttpMethod { 28 | HttpMethodGet, 29 | HttpMethodPost, 30 | HttpMethodPut, 31 | HttpMethodDelete, 32 | }; 33 | 34 | /** 35 | * Created by Thierry on 16/2/25. 36 | */ 37 | class GMNetService private constructor() { 38 | 39 | val localBroadcastManager = LocalBroadcastManager.getInstance(App.instance); 40 | val cookieManager: CookieManager = CookieManager.getInstance() 41 | val TAG = GMNetService::class.java.canonicalName 42 | var apiHost: String = Constants.Host 43 | 44 | private object Holder { 45 | val INSTANCE = GMNetService() 46 | } 47 | 48 | companion object { 49 | val instance: GMNetService by lazy { Holder.INSTANCE } 50 | } 51 | 52 | fun doRequest(remoteUrl: String, method: HttpMethod, params: List>?, success: (Response) -> Unit, failed: (String) -> Unit) { 53 | Log.d(TAG, remoteUrl) 54 | async { 55 | Manager.instance.baseHeaders = mapOf("Cookie" to cookieManager.getCookie(apiHost)) 56 | when (method) { 57 | HttpMethod.HttpMethodGet -> 58 | remoteUrl.httpGet(params).responseJson { _, response, _ -> 59 | uiThread { 60 | handleResponse(response, success, failed) 61 | } 62 | } 63 | HttpMethod.HttpMethodPost -> 64 | remoteUrl.httpPost(params).responseJson { _, response, _ -> 65 | uiThread { 66 | handleResponse(response, success, failed) 67 | } 68 | } 69 | HttpMethod.HttpMethodDelete -> 70 | remoteUrl.httpDelete(params).responseJson { _, response, _ -> 71 | uiThread { 72 | handleResponse(response, success, failed) 73 | } 74 | } 75 | HttpMethod.HttpMethodPut -> 76 | remoteUrl.httpPut(params).responseJson { request, response, result -> 77 | uiThread { 78 | handleResponse(response, success, failed) 79 | } 80 | } 81 | } 82 | 83 | } 84 | } 85 | 86 | fun handleResponse(response: Response, success: (Response) -> Unit, failed: (String) -> Unit) { 87 | Log.d(TAG, "handleResponse") 88 | val statusCode = response.httpStatusCode 89 | if (statusCode == HttpStatusCode.HttpStatusCodeSuccess.code) { 90 | success(response) 91 | } else if (statusCode == HttpStatusCode.HttpStatusCodeUnauthorized.code) { 92 | Log.d(TAG, "HttpStatusCode.HttpStatusCodeUnauthorized") 93 | this.sendLoginExpired() 94 | } else { 95 | failed("Request Data Error") 96 | } 97 | } 98 | 99 | fun sendLoginExpired() { 100 | val intent: Intent = Intent() 101 | intent.action = Constants.Broadcast.LoginExpired 102 | localBroadcastManager.sendBroadcast(intent) 103 | } 104 | 105 | } -------------------------------------------------------------------------------- /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/thierry/beaconfire/util/ButterKnife.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.util 2 | 3 | /** 4 | * Created by Thierry on 16/3/9. 5 | */ 6 | 7 | import android.app.Activity 8 | import android.app.Dialog 9 | import android.view.View 10 | import kotlin.properties.ReadOnlyProperty 11 | import kotlin.reflect.KProperty 12 | import android.app.Fragment 13 | import android.support.v7.widget.RecyclerView.ViewHolder 14 | import android.support.v4.app.Fragment as SupportFragment 15 | 16 | public fun View.bindView(id: Int) 17 | : ReadOnlyProperty = required(id, viewFinder) 18 | 19 | public fun Activity.bindView(id: Int) 20 | : ReadOnlyProperty = required(id, viewFinder) 21 | 22 | public fun Dialog.bindView(id: Int) 23 | : ReadOnlyProperty = required(id, viewFinder) 24 | 25 | public fun Fragment.bindView(id: Int) 26 | : ReadOnlyProperty = required(id, viewFinder) 27 | 28 | public fun SupportFragment.bindView(id: Int) 29 | : ReadOnlyProperty = required(id, viewFinder) 30 | 31 | public fun ViewHolder.bindView(id: Int) 32 | : ReadOnlyProperty = required(id, viewFinder) 33 | 34 | public fun View.bindOptionalView(id: Int) 35 | : ReadOnlyProperty = optional(id, viewFinder) 36 | 37 | public fun Activity.bindOptionalView(id: Int) 38 | : ReadOnlyProperty = optional(id, viewFinder) 39 | 40 | public fun Dialog.bindOptionalView(id: Int) 41 | : ReadOnlyProperty = optional(id, viewFinder) 42 | 43 | public fun Fragment.bindOptionalView(id: Int) 44 | : ReadOnlyProperty = optional(id, viewFinder) 45 | 46 | public fun SupportFragment.bindOptionalView(id: Int) 47 | : ReadOnlyProperty = optional(id, viewFinder) 48 | 49 | public fun ViewHolder.bindOptionalView(id: Int) 50 | : ReadOnlyProperty = optional(id, viewFinder) 51 | 52 | public fun View.bindViews(vararg ids: Int) 53 | : ReadOnlyProperty> = required(ids, viewFinder) 54 | 55 | public fun Activity.bindViews(vararg ids: Int) 56 | : ReadOnlyProperty> = required(ids, viewFinder) 57 | 58 | public fun Dialog.bindViews(vararg ids: Int) 59 | : ReadOnlyProperty> = required(ids, viewFinder) 60 | 61 | public fun Fragment.bindViews(vararg ids: Int) 62 | : ReadOnlyProperty> = required(ids, viewFinder) 63 | 64 | public fun SupportFragment.bindViews(vararg ids: Int) 65 | : ReadOnlyProperty> = required(ids, viewFinder) 66 | 67 | public fun ViewHolder.bindViews(vararg ids: Int) 68 | : ReadOnlyProperty> = required(ids, viewFinder) 69 | 70 | public fun View.bindOptionalViews(vararg ids: Int) 71 | : ReadOnlyProperty> = optional(ids, viewFinder) 72 | 73 | public fun Activity.bindOptionalViews(vararg ids: Int) 74 | : ReadOnlyProperty> = optional(ids, viewFinder) 75 | 76 | public fun Dialog.bindOptionalViews(vararg ids: Int) 77 | : ReadOnlyProperty> = optional(ids, viewFinder) 78 | 79 | public fun Fragment.bindOptionalViews(vararg ids: Int) 80 | : ReadOnlyProperty> = optional(ids, viewFinder) 81 | 82 | public fun SupportFragment.bindOptionalViews(vararg ids: Int) 83 | : ReadOnlyProperty> = optional(ids, viewFinder) 84 | 85 | public fun ViewHolder.bindOptionalViews(vararg ids: Int) 86 | : ReadOnlyProperty> = optional(ids, viewFinder) 87 | 88 | private val View.viewFinder: View.(Int) -> View? 89 | get() = { findViewById(it) } 90 | private val Activity.viewFinder: Activity.(Int) -> View? 91 | get() = { findViewById(it) } 92 | private val Dialog.viewFinder: Dialog.(Int) -> View? 93 | get() = { findViewById(it) } 94 | private val Fragment.viewFinder: Fragment.(Int) -> View? 95 | get() = { view.findViewById(it) } 96 | private val SupportFragment.viewFinder: SupportFragment.(Int) -> View? 97 | get() = { view!!.findViewById(it) } 98 | private val ViewHolder.viewFinder: ViewHolder.(Int) -> View? 99 | get() = { itemView.findViewById(it) } 100 | 101 | private fun viewNotFound(id: Int, desc: KProperty<*>): Nothing = 102 | throw IllegalStateException("View ID $id for '${desc.name}' not found.") 103 | 104 | @Suppress("UNCHECKED_CAST") 105 | private fun required(id: Int, finder: T.(Int) -> View?) 106 | = Lazy { t: T, desc -> t.finder(id) as V? ?: viewNotFound(id, desc) } 107 | 108 | @Suppress("UNCHECKED_CAST") 109 | private fun optional(id: Int, finder: T.(Int) -> View?) 110 | = Lazy { t: T, desc -> t.finder(id) as V? } 111 | 112 | @Suppress("UNCHECKED_CAST") 113 | private fun required(ids: IntArray, finder: T.(Int) -> View?) 114 | = Lazy { t: T, desc -> ids.map { t.finder(it) as V? ?: viewNotFound(it, desc) } } 115 | 116 | @Suppress("UNCHECKED_CAST") 117 | private fun optional(ids: IntArray, finder: T.(Int) -> View?) 118 | = Lazy { t: T, desc -> ids.map { t.finder(it) as V? }.filterNotNull() } 119 | 120 | // Like Kotlin's lazy delegate but the initializer gets the target and metadata passed to it 121 | private class Lazy(private val initializer: (T, KProperty<*>) -> V) : ReadOnlyProperty { 122 | private object EMPTY 123 | 124 | private var value: Any? = EMPTY 125 | 126 | override fun getValue(thisRef: T, property: KProperty<*>): V { 127 | if (value == EMPTY) { 128 | value = initializer(thisRef, property) 129 | } 130 | @Suppress("UNCHECKED_CAST") 131 | return value as V 132 | } 133 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thierry/beaconfire/module/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.thierry.beaconfire.module 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.IntentFilter 7 | import android.graphics.Color 8 | import android.os.Bundle 9 | import android.support.v4.content.LocalBroadcastManager 10 | import android.support.v4.widget.DrawerLayout 11 | import com.thierry.beaconfire.module.dashboard.DashboardFragment 12 | import android.widget.ListView 13 | import com.balysv.materialmenu.MaterialMenuDrawable.Stroke 14 | import com.balysv.materialmenu.MaterialMenuIcon 15 | import com.thierry.beaconfire.R 16 | import com.thierry.beaconfire.common.BaseActivity 17 | import com.thierry.beaconfire.common.BaseFragment 18 | import com.thierry.beaconfire.module.project.ProjectListFragment 19 | import com.thierry.beaconfire.module.issues.IssuesFragment 20 | import com.thierry.beaconfire.module.stats.StatsFragment 21 | import com.thierry.beaconfire.util.bindView 22 | import org.jetbrains.anko.onItemClick 23 | import android.support.v4.widget.DrawerLayout.DrawerListener 24 | import android.view.MenuItem 25 | import android.view.View 26 | import android.widget.SimpleAdapter 27 | import com.balysv.materialmenu.MaterialMenuDrawable 28 | import com.thierry.beaconfire.App 29 | import com.thierry.beaconfire.module.account.AccountFragment 30 | import com.thierry.beaconfire.util.Constants 31 | import java.util.* 32 | 33 | class MainActivity : BaseActivity(), DrawerListener { 34 | 35 | var materialMenu: MaterialMenuIcon? = null 36 | var isDrawerOpened: Boolean = false 37 | val mDrawerLayout: DrawerLayout by bindView(R.id.drawer_layout) 38 | val mFragmentTitles = arrayOf("DashBoard", "Projects", "Stats", "Issues", "Account") 39 | val mFragmentIcons = arrayOf(R.drawable.icon_drawer_dashboard, R.drawable.icon_drawer_project, R.drawable.icon_drawer_stats, R.drawable.icon_drawer_issues, R.drawable.icon_drawer_account) 40 | val mDrawerList: ListView by bindView(R.id.left_drawer) 41 | val mFragments: List = listOf(DashboardFragment(), ProjectListFragment(), StatsFragment(), IssuesFragment(), AccountFragment()) 42 | var localBroadcastManager: LocalBroadcastManager? = null 43 | var mBroadcastReceiver: AuthBroadcastReceiver? = null 44 | 45 | override fun onCreate(savedInstanceState: Bundle?) { 46 | super.onCreate(savedInstanceState) 47 | setContentView(R.layout.activity_main) 48 | actionBar.setDisplayHomeAsUpEnabled(true) 49 | this.drawLeftDrawer() 50 | this.registerBroadcast() 51 | } 52 | 53 | fun drawLeftDrawer() { 54 | materialMenu = MaterialMenuIcon(this, Color.WHITE, Stroke.THIN); 55 | mDrawerList.adapter = SimpleAdapter(this, getData(), R.layout.listitem_drawer, arrayOf("icon", "title"), intArrayOf(R.id.drawer_icon, R.id.drawer_title)) 56 | mDrawerList.onItemClick { _, _, position, _ -> 57 | this.selectItem(position) 58 | } 59 | this.selectItem(0) 60 | mDrawerLayout.setDrawerListener(this) 61 | } 62 | 63 | fun getData(): ArrayList> { 64 | val list = ArrayList>() 65 | for ((index, title: String) in mFragmentTitles.withIndex()) { 66 | val map: HashMap = HashMap() 67 | map.put("title", title) 68 | map.put("icon", mFragmentIcons[index]) 69 | list.add(map) 70 | } 71 | return list 72 | } 73 | 74 | fun registerBroadcast() { 75 | localBroadcastManager = LocalBroadcastManager.getInstance(this); 76 | mBroadcastReceiver = AuthBroadcastReceiver(this) 77 | val intentFilter: IntentFilter = IntentFilter() 78 | intentFilter.addAction(Constants.Broadcast.LoginExpired) 79 | localBroadcastManager?.registerReceiver(mBroadcastReceiver, intentFilter) 80 | } 81 | 82 | fun selectItem(position: Int) { 83 | this.replaceFragmentByTag(R.id.content_frame, mFragments[position], "fragment" + position) 84 | mDrawerList.setItemChecked(position, true) 85 | actionBar.title = mFragmentTitles[position] 86 | mDrawerLayout.closeDrawer(mDrawerList) 87 | } 88 | 89 | override fun onPostCreate(savedInstanceState: Bundle?) { 90 | super.onPostCreate(savedInstanceState) 91 | materialMenu?.syncState(savedInstanceState) 92 | } 93 | 94 | override fun onSaveInstanceState(outState: Bundle?) { 95 | materialMenu?.onSaveInstanceState(outState); 96 | super.onSaveInstanceState(outState); 97 | } 98 | 99 | override fun onDrawerSlide(drawerView: View, slideOffset: Float) { 100 | var value = slideOffset 101 | if (isDrawerOpened) { 102 | value = 2 - slideOffset 103 | } 104 | materialMenu?.setTransformationOffset(MaterialMenuDrawable.AnimationState.BURGER_ARROW, value) 105 | } 106 | 107 | override fun onDrawerOpened(drawerView: View) { 108 | isDrawerOpened = true 109 | } 110 | 111 | override fun onDrawerClosed(drawerView: View) { 112 | isDrawerOpened = false 113 | } 114 | 115 | override fun onDrawerStateChanged(newState: Int) { 116 | if (newState == DrawerLayout.STATE_IDLE) { 117 | if (isDrawerOpened) { 118 | materialMenu?.state = MaterialMenuDrawable.IconState.ARROW 119 | } else { 120 | materialMenu?.state = MaterialMenuDrawable.IconState.BURGER 121 | } 122 | } 123 | } 124 | 125 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 126 | if (item.itemId == android.R.id.home) { 127 | if (isDrawerOpened) { 128 | materialMenu?.state = MaterialMenuDrawable.IconState.ARROW 129 | mDrawerLayout.closeDrawer(mDrawerList) 130 | } else { 131 | materialMenu?.state = MaterialMenuDrawable.IconState.BURGER 132 | mDrawerLayout.openDrawer(mDrawerList) 133 | } 134 | } 135 | return super.onOptionsItemSelected(item) 136 | } 137 | 138 | override fun onDestroy() { 139 | super.onDestroy() 140 | localBroadcastManager?.unregisterReceiver(mBroadcastReceiver); 141 | } 142 | 143 | class AuthBroadcastReceiver(val context: Context) : BroadcastReceiver() { 144 | override fun onReceive(context: Context, intent: Intent?) { 145 | if (intent?.action == Constants.Broadcast.LoginExpired) { 146 | App.instance.cleanCookie() 147 | val mIntent: Intent = Intent(context, LoginActivity::class.java) 148 | mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 149 | mIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) 150 | context.startActivity(mIntent); 151 | } 152 | } 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 23 | 24 | 25 | 26 | 27 | 28 | 34 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | --------------------------------------------------------------------------------