├── .idea ├── .name ├── .gitignore ├── codeStyles │ └── codeStyleConfig.xml ├── discord.xml ├── vcs.xml └── inspectionProfiles │ └── Project_Default.xml ├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── font │ │ │ ├── manrope_vf.ttf │ │ │ ├── manrope_bold.ttf │ │ │ ├── manrope_light.ttf │ │ │ ├── manrope_medium.ttf │ │ │ ├── manrope_regular.ttf │ │ │ ├── notocoloremoji.ttf │ │ │ ├── manrope_extrabold.ttf │ │ │ ├── manrope_semibold.ttf │ │ │ ├── manrope_extralight.ttf │ │ │ └── jetbrainsmono_variable.ttf │ │ ├── drawable │ │ │ ├── github_icon.png │ │ │ ├── ic_branch.xml │ │ │ ├── ic_tag.xml │ │ │ └── ic_gimmick_icon.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ ├── values │ │ │ ├── ic_launcher_background.xml │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ └── themes.xml │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── values-night │ │ │ └── themes.xml │ │ └── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ ├── ic_launcher-playstore.png │ │ ├── java │ │ └── at │ │ │ └── sphericalk │ │ │ └── gidget │ │ │ ├── utils │ │ │ ├── Datastore.kt │ │ │ ├── LanguageColors.kt │ │ │ ├── Extensions.kt │ │ │ └── Constants.kt │ │ │ ├── GidgetApplication.kt │ │ │ ├── model │ │ │ ├── ApiResult.kt │ │ │ ├── AccessTokenResponse.kt │ │ │ ├── GithubUser.kt │ │ │ └── Events.kt │ │ │ ├── ui │ │ │ ├── theme │ │ │ │ ├── Shape.kt │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ ├── composables │ │ │ │ ├── Loading.kt │ │ │ │ └── MarkdownText.kt │ │ │ └── routes │ │ │ │ ├── FeedViewModel.kt │ │ │ │ ├── Welcome.kt │ │ │ │ ├── Release.kt │ │ │ │ └── Feed.kt │ │ │ ├── data │ │ │ ├── db │ │ │ │ ├── AppDb.kt │ │ │ │ ├── EventDao.kt │ │ │ │ └── Converters.kt │ │ │ └── network │ │ │ │ ├── GithubDataSource.kt │ │ │ │ └── GithubService.kt │ │ │ ├── navigation │ │ │ └── Destination.kt │ │ │ ├── di │ │ │ ├── DbModule.kt │ │ │ └── ApiModule.kt │ │ │ ├── AuthActivity.kt │ │ │ ├── repo │ │ │ └── GithubRepository.kt │ │ │ └── MainActivity.kt │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── keystore.jks ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle ├── .gitignore ├── README.md ├── gradle.properties ├── gradlew.bat └── gradlew /.idea/.name: -------------------------------------------------------------------------------- 1 | Gidget -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/keystore.jks -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/font/manrope_vf.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/font/manrope_vf.ttf -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/res/font/manrope_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/font/manrope_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/drawable/github_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/drawable/github_icon.png -------------------------------------------------------------------------------- /app/src/main/res/font/manrope_light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/font/manrope_light.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/manrope_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/font/manrope_medium.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/manrope_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/font/manrope_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/notocoloremoji.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/font/notocoloremoji.ttf -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/utils/Datastore.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.utils 2 | 3 | data class UserPrefs(val apiKey: String) -------------------------------------------------------------------------------- /app/src/main/res/font/manrope_extrabold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/font/manrope_extrabold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/manrope_semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/font/manrope_semibold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/manrope_extralight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/font/manrope_extralight.ttf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/font/jetbrainsmono_variable.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/font/jetbrainsmono_variable.ttf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SphericalKat/gimmick/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #1B1F23 4 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/discord.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/GidgetApplication.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget 2 | 3 | import android.app.Application 4 | import dagger.hilt.android.HiltAndroidApp 5 | 6 | @HiltAndroidApp 7 | class GidgetApplication : Application() -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jun 21 20:25:23 IST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/model/ApiResult.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.model 2 | 3 | sealed class ApiResult { 4 | data class Success(val data: T) : ApiResult() 5 | data class Error(val message: String) : ApiResult() 6 | object Loading : ApiResult() 7 | } -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Gimmick 3 | 4 | 5 | Welcome to your new and improved GitHub feed. 6 | Sign in 7 | GitHub icon 8 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 3 | repositories { 4 | google() 5 | mavenCentral() 6 | jcenter() // Warning: this repository is going to shut down soon 7 | } 8 | } 9 | rootProject.name = "Gidget" 10 | include ':app' 11 | -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/model/AccessTokenResponse.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.model 2 | 3 | import androidx.annotation.Keep 4 | 5 | @Keep 6 | data class AccessTokenResponse( 7 | val access_token: String? = null, 8 | val token_type: String? = null, 9 | val error: String? = null, 10 | val error_description: String? = null, 11 | val error_uri: String? = null, 12 | ) -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/ui/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.ui.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val Shapes = Shapes( 8 | small = RoundedCornerShape(10.dp), 9 | medium = RoundedCornerShape(10.dp), 10 | large = RoundedCornerShape(0.dp) 11 | ) -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/data/db/AppDb.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.data.db 2 | 3 | import androidx.room.Database 4 | import androidx.room.RoomDatabase 5 | import androidx.room.TypeConverters 6 | import at.sphericalk.gidget.model.Event 7 | 8 | @Database(entities = [Event::class], version = 5, exportSchema = true) 9 | @TypeConverters(Converters::class) 10 | abstract class AppDb : RoomDatabase() { 11 | abstract fun eventDao(): EventDao 12 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/codeStyles/Project.xml 10 | /.idea/compiler.xml 11 | /.idea/deploymentTargetDropDown.xml 12 | /.idea/gradle.xml 13 | /.idea/misc.xml 14 | /.idea/assetWizardSettings.xml 15 | .DS_Store 16 | /build 17 | /captures 18 | .externalNativeBuild 19 | .cxx 20 | local.properties 21 | secrets.properties 22 | .envrc 23 | app/google-services.json 24 | app/release 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_branch.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/navigation/Destination.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.navigation 2 | 3 | import androidx.navigation.NavController 4 | 5 | sealed class Destination(val route: String) { 6 | object Welcome : Destination("welcome") 7 | object Feed : Destination("feed") 8 | object Release : Destination("release") 9 | } 10 | 11 | class AppActions(navController: NavController) { 12 | val navigateToFeed: () -> Unit = { 13 | navController.navigate(Destination.Feed.route) { 14 | popUpTo(Destination.Welcome.route) { inclusive = true } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/data/db/EventDao.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.data.db 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | import androidx.room.Query 7 | import at.sphericalk.gidget.model.Event 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | @Dao 11 | interface EventDao { 12 | @Query("SELECT * FROM event ORDER BY event_created_at DESC") 13 | fun getAllEvents(): Flow> 14 | 15 | @Insert(onConflict = OnConflictStrategy.REPLACE) 16 | suspend fun insertEvent(events: Event) 17 | 18 | @Insert(onConflict = OnConflictStrategy.REPLACE) 19 | suspend fun insertEvents(events: List) 20 | } -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/utils/LanguageColors.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.utils 2 | 3 | import com.squareup.moshi.Moshi 4 | import com.squareup.moshi.Types 5 | import java.lang.reflect.ParameterizedType 6 | import javax.inject.Inject 7 | 8 | class LanguageColors @Inject constructor(moshi: Moshi) { 9 | private val mapType: ParameterizedType = Types.newParameterizedType( 10 | MutableMap::class.java, 11 | String::class.java, 12 | String::class.java 13 | ) 14 | private val mapAdapter = moshi.adapter>(mapType) 15 | var languages = mapAdapter.fromJson(Constants.LANGUAGES)!! 16 | 17 | operator fun get(lang: String) = languages[lang] 18 | } -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/ui/composables/Loading.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.ui.composables 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.fillMaxSize 6 | import androidx.compose.material.CircularProgressIndicator 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Alignment 9 | import androidx.compose.ui.Modifier 10 | 11 | @Composable 12 | fun Loading() { 13 | Column( 14 | Modifier.fillMaxSize(), 15 | verticalArrangement = Arrangement.Center, 16 | horizontalAlignment = Alignment.CenterHorizontally 17 | ) { 18 | CircularProgressIndicator() 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_tag.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/data/network/GithubDataSource.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.data.network 2 | 3 | import javax.inject.Inject 4 | 5 | class GithubDataSource @Inject constructor(private val service: GithubService) { 6 | suspend fun getReceivedEvents(token: String, username: String) = 7 | service.getReceivedEvents(token = "token $token", username = username) 8 | 9 | suspend fun getRepo(token: String, owner: String, name: String) = 10 | service.getRepo(token = "token $token", owner = owner, name = name) 11 | 12 | suspend fun getAccessToken( 13 | clientId: String, 14 | clientSecret: String, 15 | code: String, 16 | redirectUrl: String 17 | ) = service.getAccessToken(clientId, clientSecret, code, redirectUrl) 18 | 19 | suspend fun getUser(token: String) = service.getUser(token) 20 | } -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gimmick 2 | A while back, I noticed that the official GitHub app doesn't have a feed similar to the one can find on the GitHub homepage. Naturally I had to make it myself. 3 | 4 | This app is built using Jetpack Compose, Hilt, Kotlin Flow, Room and Retrofit. 5 | 6 | # Building 7 | You will need to create or obtain a `secrets.properties` file, located at the root of the project. This file will contain your GitHub OAuth app credentials. The format for this file is as given below: 8 | 9 | ```properties 10 | CLIENT_ID="" // these quotes are important! 11 | CLIENT_SECRET="" // these quotes are important! 12 | ``` 13 | 14 | > The Oauth callback URL should be set to `gimmick://auth` 15 | 16 | # Contribution 17 | As always, I'm open to pull requests and issues. I will do my best to address these, but might not always be available thanks to work and other priorities. -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/di/DbModule.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.di 2 | 3 | import android.content.Context 4 | import androidx.room.Room 5 | import at.sphericalk.gidget.data.db.AppDb 6 | import at.sphericalk.gidget.data.db.Converters 7 | import dagger.Module 8 | import dagger.Provides 9 | import dagger.hilt.InstallIn 10 | import dagger.hilt.android.qualifiers.ApplicationContext 11 | import dagger.hilt.components.SingletonComponent 12 | import javax.inject.Singleton 13 | 14 | @Module 15 | @InstallIn(SingletonComponent::class) 16 | object DbModule { 17 | @Provides 18 | @Singleton 19 | fun providesAppDb(@ApplicationContext context: Context, converters: Converters) = 20 | Room.databaseBuilder(context, AppDb::class.java, "gimmick-db") 21 | .addTypeConverter(converters) 22 | .fallbackToDestructiveMigration() 23 | .build() 24 | 25 | @Provides 26 | @Singleton 27 | fun providesEventDao(db: AppDb) = db.eventDao() 28 | } -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/utils/Extensions.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.utils 2 | 3 | import android.text.format.DateUtils 4 | import android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE 5 | import android.util.Log 6 | import androidx.compose.ui.graphics.Color 7 | import java.text.SimpleDateFormat 8 | import java.util.* 9 | 10 | fun List.destructure() = Pair(get(0), get(1)) 11 | 12 | fun String.toColor() = Color(android.graphics.Color.parseColor(this)) 13 | 14 | fun String.timeAgo(): String { 15 | return try { 16 | val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") 17 | sdf.timeZone = TimeZone.getTimeZone("UTC") 18 | val date = sdf.parse(this)!! 19 | DateUtils.getRelativeTimeSpanString( 20 | date.time, 21 | Date().time, 22 | DateUtils.MINUTE_IN_MILLIS, 23 | FORMAT_ABBREV_RELATIVE 24 | ).toString() 25 | } catch (e: Exception) { 26 | Log.e("DATE", "Something went wrong while getting relative time: ", e) 27 | "" 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/model/GithubUser.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.model 2 | 3 | import androidx.annotation.Keep 4 | 5 | @Keep 6 | data class GithubUser( 7 | val login: String, 8 | val id: Int, 9 | val node_id: String, 10 | val avatar_url: String, 11 | val gravatar_id: String, 12 | val url: String, 13 | val html_url: String, 14 | val followers_url: String, 15 | val following_url: String, 16 | val gists_url: String, 17 | val starred_url: String, 18 | val subscriptions_url: String, 19 | val organizations_url: String, 20 | val repos_url: String, 21 | val events_url: String, 22 | val received_events_url: String, 23 | val type: String, 24 | val site_admin: Boolean, 25 | val name: String, 26 | val company: String, 27 | val blog: String, 28 | val location: String, 29 | val email: String, 30 | val bio: String, 31 | val twitter_username: String, 32 | val public_repos: Int, 33 | val public_gists: Int, 34 | val followers: Int, 35 | val following: Int, 36 | val created_at: String, 37 | val updated_at: String 38 | ) -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=false 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | -dontwarn org.bouncycastle.jsse.BCSSLParameters 24 | -dontwarn org.bouncycastle.jsse.BCSSLSocket 25 | -dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider 26 | -dontwarn org.conscrypt.Conscrypt$Version 27 | -dontwarn org.conscrypt.Conscrypt 28 | -dontwarn org.conscrypt.ConscryptHostnameVerifier 29 | -dontwarn org.openjsse.javax.net.ssl.SSLParameters 30 | -dontwarn org.openjsse.javax.net.ssl.SSLSocket 31 | -dontwarn org.openjsse.net.ssl.OpenJSSE -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/AuthActivity.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget 2 | 3 | import android.app.Activity 4 | import android.content.Intent 5 | import android.net.Uri 6 | import android.os.Bundle 7 | import androidx.activity.ComponentActivity 8 | import at.sphericalk.gidget.utils.Constants 9 | 10 | class AuthActivity : ComponentActivity() { 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | 15 | val authIntent = Intent(Intent.ACTION_VIEW, Uri.parse(Constants.OAUTH_URL)) 16 | startActivity(authIntent) 17 | return 18 | } 19 | 20 | override fun onNewIntent(intent: Intent?) { 21 | super.onNewIntent(intent) 22 | 23 | if (intent == null) { 24 | setResult(Activity.RESULT_CANCELED) 25 | finish() 26 | return 27 | } 28 | 29 | val uri = intent.data 30 | val returnIntent = Intent().apply { data = intent.data } 31 | 32 | if (uri.toString().startsWith(Constants.REDIRECT_URI)) { 33 | setResult(Activity.RESULT_OK, returnIntent) 34 | } else { 35 | setResult(Activity.RESULT_CANCELED, returnIntent) 36 | } 37 | 38 | finish() 39 | } 40 | 41 | override fun onDestroy() { 42 | super.onDestroy() 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | 6 | interface ColorType { 7 | val background: Color 8 | val backgroundElevated: Color 9 | val backgroundSecondary: Color 10 | 11 | val primaryColor: Color 12 | 13 | val textPrimary: Color 14 | val textSecondary: Color 15 | 16 | val textFieldBackground: Color 17 | } 18 | 19 | object LightColors : ColorType { 20 | override val background = Color(0xfff0f0f0) 21 | override val backgroundElevated = Color(0xfff5f5f5) 22 | override val backgroundSecondary = Color.White 23 | 24 | override val primaryColor = Color(0xff1f99b7) 25 | 26 | override val textPrimary = Color(0xff141414) 27 | override val textSecondary = Color(0xff444444) 28 | 29 | override val textFieldBackground = Color(0x14212121) 30 | } 31 | 32 | object DarkColors : ColorType { 33 | override val background = Color(0xff111517) 34 | override val backgroundElevated = Color(0xff1F2022) 35 | override val backgroundSecondary = Color(0xff020202) 36 | 37 | override val primaryColor = Color(0xff4dd0e1) 38 | 39 | override val textPrimary = Color(0xfff9f9f9) 40 | override val textSecondary = Color(0xffcccccc) 41 | 42 | override val textFieldBackground = Color(0x14ffffff) 43 | } -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/data/network/GithubService.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.data.network 2 | 3 | import at.sphericalk.gidget.model.AccessTokenResponse 4 | import at.sphericalk.gidget.model.Event 5 | import at.sphericalk.gidget.model.GithubUser 6 | import at.sphericalk.gidget.model.RepoExtra 7 | import retrofit2.http.* 8 | 9 | interface GithubService { 10 | @Headers("Accept: application/json") 11 | @POST("https://github.com/login/oauth/access_token") 12 | @FormUrlEncoded 13 | suspend fun getAccessToken( 14 | @Field("client_id") clientId: String, 15 | @Field("client_secret") clientSecret: String, 16 | @Field("code") code: String, 17 | @Field("redirect_uri") redirectUrl: String 18 | ): AccessTokenResponse 19 | 20 | @GET("/users/{username}/received_events") 21 | suspend fun getReceivedEvents( 22 | @Header("accept") accept: String = "application/vnd.github.v3+json", 23 | @Header("Authorization") token: String, 24 | @Path("username") username: String, 25 | @Query("per_page") perPage: Int = 60 26 | ): List 27 | 28 | @GET("/user") 29 | suspend fun getUser( 30 | @Header("Authorization") token: String, 31 | ): GithubUser 32 | 33 | @GET("/repos/{owner}/{name}") 34 | suspend fun getRepo( 35 | @Header("accept") accept: String = "application/vnd.github.v3+json", 36 | @Header("Authorization") token: String, 37 | @Path("owner") owner: String, 38 | @Path("name") name: String, 39 | ): RepoExtra 40 | } -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/di/ApiModule.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.di 2 | 3 | import at.sphericalk.gidget.data.network.GithubService 4 | import com.squareup.moshi.Moshi 5 | import dagger.Module 6 | import dagger.Provides 7 | import dagger.hilt.InstallIn 8 | import dagger.hilt.components.SingletonComponent 9 | import okhttp3.OkHttpClient 10 | import okhttp3.logging.HttpLoggingInterceptor 11 | import okhttp3.logging.HttpLoggingInterceptor.Level 12 | import retrofit2.Retrofit 13 | import retrofit2.converter.moshi.MoshiConverterFactory 14 | import javax.inject.Singleton 15 | 16 | 17 | private const val BASE_URL = "https://api.github.com" 18 | 19 | @Module 20 | @InstallIn(SingletonComponent::class) 21 | object ApiModule { 22 | @Provides 23 | @Singleton 24 | fun providesRetrofitService(client: OkHttpClient): Retrofit = Retrofit.Builder() 25 | .baseUrl(BASE_URL) 26 | // .client(client) 27 | .addConverterFactory(MoshiConverterFactory.create()) 28 | .build() 29 | 30 | 31 | @Provides 32 | @Singleton 33 | fun providesGithubService(retrofit: Retrofit): GithubService = 34 | retrofit.create(GithubService::class.java) 35 | 36 | @Provides 37 | @Singleton 38 | fun providesOkHttpClient(): OkHttpClient { 39 | val logging = HttpLoggingInterceptor() 40 | logging.setLevel(Level.BODY) 41 | return OkHttpClient.Builder() 42 | .addInterceptor(logging) 43 | .build() 44 | } 45 | 46 | @Provides 47 | @Singleton 48 | fun providesMoshiInstance() = Moshi.Builder().build() 49 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/at/sphericalk/gidget/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package at.sphericalk.gidget.ui.theme 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.darkColors 6 | import androidx.compose.material.lightColors 7 | import androidx.compose.runtime.Composable 8 | 9 | private val DarkColorPalette = darkColors( 10 | primary = DarkColors.primaryColor, 11 | primaryVariant = DarkColors.primaryColor, 12 | secondary = DarkColors.primaryColor, 13 | secondaryVariant = DarkColors.primaryColor, 14 | surface = DarkColors.backgroundElevated, 15 | onBackground = DarkColors.background, 16 | onSurface = DarkColors.textPrimary, 17 | background = DarkColors.background, 18 | onPrimary = DarkColors.background, 19 | onSecondary = DarkColors.background, 20 | ) 21 | 22 | private val LightColorPalette = lightColors( 23 | primary = LightColors.primaryColor, 24 | primaryVariant = LightColors.primaryColor, 25 | secondary = LightColors.primaryColor, 26 | secondaryVariant = LightColors.primaryColor, 27 | surface = LightColors.backgroundSecondary, 28 | onBackground = LightColors.background, 29 | onSurface = LightColors.textPrimary, 30 | background = LightColors.background, 31 | onPrimary = LightColors.background, 32 | onSecondary = LightColors.background, 33 | ) 34 | 35 | @Composable 36 | fun GidgetTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { 37 | val colors = if (darkTheme) { 38 | DarkColorPalette 39 | } else { 40 | LightColorPalette 41 | } 42 | 43 | val colorType = if (darkTheme) DarkColors else LightColors 44 | 45 | MaterialTheme( 46 | colors = colors, 47 | typography = buildTypography(colorType), 48 | shapes = Shapes, 49 | content = content 50 | ) 51 | } -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 27 | 28 | 32 | 33 |