...
@ -136,6 +136,7 @@ dependencies {
|
||||
"include" to listOf("*.aar", "*.jar"),
|
||||
)))
|
||||
implementation(project(":gdrive"))
|
||||
implementation("com.google.android.gms:play-services-wearable:19.0.0")
|
||||
val kotlinVersion: String? by extra
|
||||
val realmVersion = "2.0.0"
|
||||
implementation ("androidx.appcompat:appcompat:1.7.1")
|
||||
|
||||
@ -146,6 +146,10 @@
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name=".workers.WatchGestureService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name=".workers.TorrentService"
|
||||
|
||||
120
app/src/main/assets/extensions/my_extension/adfskldfslkjdfs.js
Normal file
@ -0,0 +1,120 @@
|
||||
(function() {
|
||||
var retryCount = 0;
|
||||
var maxRetries = 2;
|
||||
|
||||
function tryExtract() {
|
||||
try {
|
||||
const newUrl = window.location.origin + window.location.pathname;
|
||||
const hostname = window.location.hostname;
|
||||
const contentsType = hostname.includes("book") ? "booktoki" :
|
||||
hostname.includes("mana") ? "manatoki" :
|
||||
hostname.includes("new") ? "newtoki" : "web";
|
||||
|
||||
// 1. 목차(List) 감지 로직
|
||||
const listBody = document.querySelector(".list-body");
|
||||
const novelContent = document.querySelector("#novel_content");
|
||||
|
||||
// [중요] 두 핵심 요소가 모두 없으면 재시도 결정
|
||||
if (!listBody && !novelContent) {
|
||||
if (retryCount < maxRetries) {
|
||||
retryCount++;
|
||||
console.log("DEBUG_LOG: [JS] 요소를 찾지 못해 재시도 중... (" + retryCount + ")");
|
||||
setTimeout(tryExtract, 500); // 0.5초 후 다시 실행
|
||||
return;
|
||||
} else {
|
||||
console.log("DEBUG_LOG: [JS] 최대 재시도 횟수 초과");
|
||||
window.webkit.messageHandlers.ContentsRcv.postMessage(JSON.stringify({
|
||||
'type': 'JP',
|
||||
'currentUrl' : newUrl
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// --- 1. 목차 추출 실행 ---
|
||||
if (listBody !== null) {
|
||||
const contentsArray = [];
|
||||
const children = listBody.children;
|
||||
const bookTitleEl = document.querySelector('.view-title span');
|
||||
const bookTitle = bookTitleEl ? bookTitleEl.innerText.trim() : "Unknown";
|
||||
const pagination = document.querySelector(".pagination");
|
||||
let nextPagingUrl = null;
|
||||
|
||||
if (pagination) {
|
||||
const activeLi = pagination.querySelector("li.active");
|
||||
if (activeLi && activeLi.nextElementSibling) {
|
||||
const nextA = activeLi.nextElementSibling.querySelector("a");
|
||||
// 다음 엘리먼트가 존재하고, 'disabled' 클래스가 없으며, href가 있는 경우
|
||||
if (nextA && nextA.href && !activeLi.nextElementSibling.classList.contains("disabled")) {
|
||||
nextPagingUrl = nextA.href;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
try {
|
||||
const wrNumEl = children[i].getElementsByClassName('wr-num')[0];
|
||||
const wrSubjectA = children[i].getElementsByClassName('wr-subject')[0]?.getElementsByTagName('a')[0];
|
||||
if (!wrNumEl || !wrSubjectA) continue;
|
||||
|
||||
let pageUrl = new URL(wrSubjectA.href, window.location.origin).pathname;
|
||||
let paths = pageUrl.split('/').filter(p => p);
|
||||
if (paths.length > 0) {
|
||||
var lastPart = paths[paths.length - 1];
|
||||
var isOnlyNumber = lastPart && Array.from(lastPart).every(ch => ch >= '0' && ch <= '9');
|
||||
if (!isOnlyNumber) paths.pop();
|
||||
}
|
||||
pageUrl = "/" + paths.join('/');
|
||||
|
||||
contentsArray.push({
|
||||
'chapterID': parseInt(wrNumEl.textContent.replace(/[^0-9]/g, "")),
|
||||
'chapterNum': parseInt(wrNumEl.textContent.replace(/[^0-9]/g, "")),
|
||||
'pathUrl': pageUrl,
|
||||
'contentsType': contentsType,
|
||||
'bookPageUrl': window.location.pathname,
|
||||
'chapterTitle': wrSubjectA.innerText.split('\n').pop().trim(),
|
||||
'bookTitle': bookTitle,
|
||||
'currentUrl' : newUrl
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (contentsArray.length > 0) {
|
||||
window.webkit.messageHandlers.ContentsRcv.postMessage(JSON.stringify({
|
||||
'type': 'getListResult',
|
||||
'contentsType': contentsType,
|
||||
'bookTitle': bookTitle,
|
||||
'bookPageUrl': window.location.pathname,
|
||||
'pages': contentsArray,
|
||||
'currentUrl' : newUrl,
|
||||
'nextPagingUrl': nextPagingUrl
|
||||
}));
|
||||
return; // 전송 성공 시 종료
|
||||
}
|
||||
}
|
||||
|
||||
// --- 2. 본문 추출 실행 ---
|
||||
if (novelContent !== null) {
|
||||
const titleEl = document.querySelector(".page-desc");
|
||||
const chapterTitle = titleEl ? titleEl.innerText.trim() : "";
|
||||
const contents = novelContent.innerText.trim();
|
||||
|
||||
|
||||
if (contents.length > 100) {
|
||||
window.webkit.messageHandlers.ContentsRcv.postMessage(JSON.stringify({
|
||||
'type': 'BookContents',
|
||||
'chapterTitle': chapterTitle,
|
||||
'bookContents': contents,
|
||||
'pageUrl' : window.location.pathname,
|
||||
'currentUrl' : newUrl
|
||||
}));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Unified Script Error: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
// 최초 실행
|
||||
tryExtract();
|
||||
})();
|
||||
@ -0,0 +1,25 @@
|
||||
package bums.lunatic.launcher.workers
|
||||
|
||||
import bums.lunatic.launcher.utils.Blog
|
||||
import com.google.android.gms.wearable.MessageEvent
|
||||
import com.google.android.gms.wearable.WearableListenerService
|
||||
|
||||
class WatchGestureService : WearableListenerService() {
|
||||
override fun onMessageReceived(messageEvent: MessageEvent) {
|
||||
when (messageEvent.path) {
|
||||
"/gesture/next" -> {
|
||||
// 런처의 다음 페이지로 이동하거나, 이북 앱에 키 이벤트 전송
|
||||
handleGestureAction("NEXT")
|
||||
}
|
||||
"/gesture/prev" -> {
|
||||
handleGestureAction("PREV")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleGestureAction(action: String) {
|
||||
// Broadcast를 런처 메인 Activity로 쏘거나,
|
||||
// 직접 AccessibilityService를 호출하여 시스템 이벤트를 발생시킵니다.
|
||||
Blog.LOGE("WatchGesture", "Gesture Received: $action")
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,7 @@ plugins {
|
||||
id ("com.android.library") version "8.10.1" apply false
|
||||
id ("io.realm.kotlin") version "2.0.0" apply false
|
||||
id("org.jetbrains.kotlin.android") version kotlinVersion apply false
|
||||
id("org.jetbrains.kotlin.plugin.compose") version kotlinVersion apply false
|
||||
|
||||
}
|
||||
|
||||
|
||||
1
lun_launcher/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
||||
73
lun_launcher/build.gradle.kts
Normal file
@ -0,0 +1,73 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "bums.lunatic.launcher"
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "bums.lunatic.launcher"
|
||||
minSdk = 30
|
||||
targetSdk = 36
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
signingConfigs {
|
||||
|
||||
getByName("debug") {
|
||||
storeFile = file("./bs_debug.keystore")
|
||||
storePassword = "android"
|
||||
keyAlias = "androiddebugkey"
|
||||
keyPassword = "android"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation("com.google.android.gms:play-services-wearable:19.0.0")
|
||||
implementation(platform("androidx.compose:compose-bom:2024.09.00"))
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-graphics")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.wear.compose:compose-material:1.2.1")
|
||||
implementation("androidx.wear.compose:compose-foundation:1.2.1")
|
||||
implementation("androidx.wear:wear-tooling-preview:1.0.0")
|
||||
implementation("androidx.activity:activity-compose:1.12.4")
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
implementation("androidx.wear.tiles:tiles:1.4.0")
|
||||
implementation("androidx.wear.tiles:tiles-material:1.4.0")
|
||||
implementation("androidx.wear.tiles:tiles-tooling-preview:1.4.0")
|
||||
implementation("com.google.android.horologist:horologist-compose-tools:0.6.17")
|
||||
implementation("com.google.android.horologist:horologist-tiles:0.6.17")
|
||||
implementation("androidx.wear.watchface:watchface-complications-data-source-ktx:1.2.1")
|
||||
androidTestImplementation(platform("androidx.compose:compose-bom:2024.09.00"))
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
debugImplementation("androidx.wear.tiles:tiles-tooling:1.4.0")
|
||||
}
|
||||
8
lun_launcher/lint.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
<!-- Ignore the IconLocation for the Tile preview images -->
|
||||
<issue id="IconLocation">
|
||||
<ignore path="res/drawable/tile_preview.png" />
|
||||
<ignore path="res/drawable-round/tile_preview.png" />
|
||||
</issue>
|
||||
</lint>
|
||||
21
lun_launcher/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
68
lun_launcher/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-feature android:name="android.hardware.type.watch" />
|
||||
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.DeviceDefault">
|
||||
<service
|
||||
android:name=".complication.MainComplicationService"
|
||||
android:exported="true"
|
||||
android:label="@string/complication_label"
|
||||
android:permission="com.google.android.wearable.permission.BIND_COMPLICATION_PROVIDER">
|
||||
<intent-filter>
|
||||
<action android:name="android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.wearable.complications.SUPPORTED_TYPES"
|
||||
android:value="SHORT_TEXT" />
|
||||
<meta-data
|
||||
android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
|
||||
android:value="0" />
|
||||
</service>
|
||||
<service
|
||||
android:name=".tile.MainTileService"
|
||||
android:exported="true"
|
||||
android:label="@string/tile_label"
|
||||
android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="androidx.wear.tiles.PREVIEW"
|
||||
android:resource="@drawable/tile_preview" />
|
||||
</service>
|
||||
|
||||
<uses-library
|
||||
android:name="com.google.android.wearable"
|
||||
android:required="true" />
|
||||
<!--
|
||||
Set to true if your app is Standalone, that is, it does not require the handheld
|
||||
app to run.
|
||||
-->
|
||||
<meta-data
|
||||
android:name="com.google.android.wearable.standalone"
|
||||
android:value="true" />
|
||||
|
||||
<activity
|
||||
android:name=".presentation.MainActivity"
|
||||
android:exported="true"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/MainActivityTheme.Starting">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@ -0,0 +1,41 @@
|
||||
package bums.lunatic.launcher.complication
|
||||
|
||||
import androidx.wear.watchface.complications.data.ComplicationData
|
||||
import androidx.wear.watchface.complications.data.ComplicationType
|
||||
import androidx.wear.watchface.complications.data.PlainComplicationText
|
||||
import androidx.wear.watchface.complications.data.ShortTextComplicationData
|
||||
import androidx.wear.watchface.complications.datasource.ComplicationRequest
|
||||
import androidx.wear.watchface.complications.datasource.SuspendingComplicationDataSourceService
|
||||
import java.util.Calendar
|
||||
|
||||
/**
|
||||
* Skeleton for complication data source that returns short text.
|
||||
*/
|
||||
class MainComplicationService : SuspendingComplicationDataSourceService() {
|
||||
|
||||
override fun getPreviewData(type: ComplicationType): ComplicationData? {
|
||||
if (type != ComplicationType.SHORT_TEXT) {
|
||||
return null
|
||||
}
|
||||
return createComplicationData("Mon", "Monday")
|
||||
}
|
||||
|
||||
override suspend fun onComplicationRequest(request: ComplicationRequest): ComplicationData {
|
||||
return when (Calendar.getInstance().get(Calendar.DAY_OF_WEEK)) {
|
||||
Calendar.SUNDAY -> createComplicationData("Sun", "Sunday")
|
||||
Calendar.MONDAY -> createComplicationData("Mon", "Monday")
|
||||
Calendar.TUESDAY -> createComplicationData("Tue", "Tuesday")
|
||||
Calendar.WEDNESDAY -> createComplicationData("Wed", "Wednesday")
|
||||
Calendar.THURSDAY -> createComplicationData("Thu", "Thursday")
|
||||
Calendar.FRIDAY -> createComplicationData("Fri!", "Friday!")
|
||||
Calendar.SATURDAY -> createComplicationData("Sat", "Saturday")
|
||||
else -> throw IllegalArgumentException("too many days")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createComplicationData(text: String, contentDescription: String) =
|
||||
ShortTextComplicationData.Builder(
|
||||
text = PlainComplicationText.Builder(text).build(),
|
||||
contentDescription = PlainComplicationText.Builder(contentDescription).build()
|
||||
).build()
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
/* While this template provides a good starting point for using Wear Compose, you can always
|
||||
* take a look at https://github.com/android/wear-os-samples/tree/main/ComposeStarter to find the
|
||||
* most up to date changes to the libraries and their usages.
|
||||
*/
|
||||
|
||||
package bums.lunatic.launcher.presentation
|
||||
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.wear.compose.material.MaterialTheme
|
||||
import androidx.wear.compose.material.Text
|
||||
import androidx.wear.compose.material.TimeText
|
||||
import androidx.wear.tooling.preview.devices.WearDevices
|
||||
import bums.lunatic.launcher.R
|
||||
import bums.lunatic.launcher.presentation.theme.LunarLauncherTheme
|
||||
import com.google.android.gms.wearable.MessageClient
|
||||
import com.google.android.gms.wearable.Wearable
|
||||
|
||||
class MainActivity : ComponentActivity() , SensorEventListener {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
installSplashScreen()
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setTheme(android.R.style.Theme_DeviceDefault)
|
||||
|
||||
setContent {
|
||||
WearApp("Android")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
|
||||
|
||||
}
|
||||
private lateinit var messageClient: MessageClient
|
||||
private var lastX = 0f
|
||||
override fun onSensorChanged(p0: SensorEvent?) {
|
||||
p0?.let { event ->
|
||||
val x = event.values[0]
|
||||
|
||||
// 아주 단순한 Threshold 예시 (나중에 DTW로 교체)
|
||||
if (x > 15f) { // 오른쪽으로 강하게 휘둘렀을 때
|
||||
sendGestureToLauncher("/gesture/next")
|
||||
} else if (x < -15f) {
|
||||
sendGestureToLauncher("/gesture/prev")
|
||||
}
|
||||
lastX = x
|
||||
}
|
||||
}
|
||||
private fun sendGestureToLauncher(path: String) {
|
||||
// 연결된 노드(폰)를 찾아 메시지 전송
|
||||
Wearable.getNodeClient(this).connectedNodes.addOnSuccessListener { nodes ->
|
||||
for (node in nodes) {
|
||||
Wearable.getMessageClient(this).sendMessage(node.id, path, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WearApp(greetingName: String) {
|
||||
LunarLauncherTheme {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colors.background),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
TimeText()
|
||||
Greeting(greetingName = greetingName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Greeting(greetingName: String) {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colors.primary,
|
||||
text = stringResource(R.string.hello_world, greetingName)
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(device = WearDevices.SMALL_ROUND, showSystemUi = true)
|
||||
@Composable
|
||||
fun DefaultPreview() {
|
||||
WearApp("Preview Android")
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package bums.lunatic.launcher.presentation.theme
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.wear.compose.material.MaterialTheme
|
||||
|
||||
@Composable
|
||||
fun LunarLauncherTheme(
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
/**
|
||||
* Empty theme to customize for your app.
|
||||
* See: https://developer.android.com/jetpack/compose/designsystems/custom
|
||||
*/
|
||||
MaterialTheme(
|
||||
content = content
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package bums.lunatic.launcher.tile
|
||||
|
||||
import android.content.Context
|
||||
import androidx.wear.protolayout.ColorBuilders.argb
|
||||
import androidx.wear.protolayout.LayoutElementBuilders
|
||||
import androidx.wear.protolayout.ResourceBuilders
|
||||
import androidx.wear.protolayout.TimelineBuilders
|
||||
import androidx.wear.protolayout.material.Colors
|
||||
import androidx.wear.protolayout.material.Text
|
||||
import androidx.wear.protolayout.material.Typography
|
||||
import androidx.wear.protolayout.material.layouts.PrimaryLayout
|
||||
import androidx.wear.tiles.RequestBuilders
|
||||
import androidx.wear.tiles.TileBuilders
|
||||
import androidx.wear.tiles.tooling.preview.Preview
|
||||
import androidx.wear.tiles.tooling.preview.TilePreviewData
|
||||
import androidx.wear.tooling.preview.devices.WearDevices
|
||||
import com.google.android.horologist.annotations.ExperimentalHorologistApi
|
||||
import com.google.android.horologist.tiles.SuspendingTileService
|
||||
|
||||
private const val RESOURCES_VERSION = "0"
|
||||
|
||||
/**
|
||||
* Skeleton for a tile with no images.
|
||||
*/
|
||||
@OptIn(ExperimentalHorologistApi::class)
|
||||
class MainTileService : SuspendingTileService() {
|
||||
|
||||
override suspend fun resourcesRequest(
|
||||
requestParams: RequestBuilders.ResourcesRequest
|
||||
) = resources(requestParams)
|
||||
|
||||
override suspend fun tileRequest(
|
||||
requestParams: RequestBuilders.TileRequest
|
||||
) = tile(requestParams, this)
|
||||
}
|
||||
|
||||
private fun resources(
|
||||
requestParams: RequestBuilders.ResourcesRequest
|
||||
): ResourceBuilders.Resources {
|
||||
return ResourceBuilders.Resources.Builder()
|
||||
.setVersion(RESOURCES_VERSION)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun tile(
|
||||
requestParams: RequestBuilders.TileRequest,
|
||||
context: Context,
|
||||
): TileBuilders.Tile {
|
||||
val singleTileTimeline = TimelineBuilders.Timeline.Builder()
|
||||
.addTimelineEntry(
|
||||
TimelineBuilders.TimelineEntry.Builder()
|
||||
.setLayout(
|
||||
LayoutElementBuilders.Layout.Builder()
|
||||
.setRoot(tileLayout(requestParams, context))
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
return TileBuilders.Tile.Builder()
|
||||
.setResourcesVersion(RESOURCES_VERSION)
|
||||
.setTileTimeline(singleTileTimeline)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun tileLayout(
|
||||
requestParams: RequestBuilders.TileRequest,
|
||||
context: Context,
|
||||
): LayoutElementBuilders.LayoutElement {
|
||||
return PrimaryLayout.Builder(requestParams.deviceConfiguration)
|
||||
.setResponsiveContentInsetEnabled(true)
|
||||
.setContent(
|
||||
Text.Builder(context, "Hello World!")
|
||||
.setColor(argb(Colors.DEFAULT.onSurface))
|
||||
.setTypography(Typography.TYPOGRAPHY_CAPTION1)
|
||||
.build()
|
||||
).build()
|
||||
}
|
||||
|
||||
@Preview(device = WearDevices.SMALL_ROUND)
|
||||
@Preview(device = WearDevices.LARGE_ROUND)
|
||||
fun tilePreview(context: Context) = TilePreviewData(::resources) {
|
||||
tile(it, context)
|
||||
}
|
||||
BIN
lun_launcher/src/main/res/drawable-round/tile_preview.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
170
lun_launcher/src/main/res/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
||||
@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
27
lun_launcher/src/main/res/drawable/splash_icon.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:gravity="center">
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="#FFFFFF" />
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:gravity="center">
|
||||
<vector
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#000000"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M17.6,11.48 L19.44,8.3a0.63,0.63 0,0 0,-1.09 -0.63l-1.88,3.24a11.43,11.43 0,0 0,-8.94 0L5.65,7.67a0.63,0.63 0,0 0,-1.09 0.63L6.4,11.48A10.81,10.81 0,0 0,1 20L23,20A10.81,10.81 0,0 0,17.6 11.48ZM7,17.25A1.25,1.25 0,1 1,8.25 16,1.25 1.25,0 0,1 7,17.25ZM17,17.25A1.25,1.25 0,1 1,18.25 16,1.25 1.25,0 0,1 17,17.25Z" />
|
||||
</vector>
|
||||
</item>
|
||||
</layer-list>
|
||||
BIN
lun_launcher/src/main/res/drawable/tile_preview.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
6
lun_launcher/src/main/res/mipmap-anydpi/ic_launcher.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
BIN
lun_launcher/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
lun_launcher/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
lun_launcher/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 982 B |
BIN
lun_launcher/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
lun_launcher/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
lun_launcher/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
lun_launcher/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
lun_launcher/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
lun_launcher/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
lun_launcher/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
3
lun_launcher/src/main/res/values-round/strings.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="hello_world">From the Round world,\nHello, %1$s!</string>
|
||||
</resources>
|
||||
10
lun_launcher/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<resources>
|
||||
<string name="app_name">lunLauncher</string>
|
||||
<!--
|
||||
This string is used for square devices and overridden by hello_world in
|
||||
values-round/strings.xml for round devices.
|
||||
-->
|
||||
<string name="hello_world">From the Square world,\nHello, %1$s!</string>
|
||||
<string name="tile_label">Example tile</string>
|
||||
<string name="complication_label">Example complication</string>
|
||||
</resources>
|
||||
8
lun_launcher/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<resources>
|
||||
|
||||
<style name="MainActivityTheme.Starting" parent="Theme.SplashScreen">
|
||||
<item name="windowSplashScreenBackground">@android:color/black</item>
|
||||
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
|
||||
<item name="postSplashScreenTheme">@android:style/Theme.DeviceDefault</item>
|
||||
</style>
|
||||
</resources>
|
||||
@ -24,3 +24,4 @@ dependencyResolutionManagement {
|
||||
rootProject.name = "LunarLauncher"
|
||||
include ("app","library","utils")
|
||||
include(":gdrive")
|
||||
include(":lun_launcher")
|
||||
|
||||