Initial commit: Minimal Flutter timer with push notifications

This commit is contained in:
Developer
2026-01-09 00:20:38 +08:00
commit 2a2f718a4b
26 changed files with 1050 additions and 0 deletions

65
android/app/build.gradle Normal file
View File

@@ -0,0 +1,65 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
def javaVersion = JavaVersion.VERSION_17
android {
namespace "com.example.timer"
compileSdk 34
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility javaVersion
targetCompatibility javaVersion
}
kotlinOptions {
jvmTarget = javaVersion.toString()
}
sourceSets {
main.java.srcDirs += 'src/main/java'
}
defaultConfig {
applicationId "com.example.timer"
minSdkVersion flutter.minSdkVersion
targetSdk 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
}

View File

@@ -0,0 +1,42 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.timer">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:label="Таймер"
android:name="${applicationName}"
android:icon="@drawable/ic_launcher_foreground">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<receiver
android:name="com.dexterous.flutterlocalnotifications.NotificationActionReceiver"
android:exported="false">
<intent-filter>
<action android:name="ACTION_NOTIFICATION_TRIGGERED" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@@ -0,0 +1,24 @@
package com.example.timer;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Launch Flutter activity using reflection
try {
Class<?> flutterActivityClass = Class.forName("io.flutter.embedding.android.FlutterActivity");
Intent intent = new Intent(this, flutterActivityClass);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} catch (ClassNotFoundException e) {
// FlutterActivity not found - handle gracefully
e.printStackTrace();
}
finish();
}
}

View File

@@ -0,0 +1,16 @@
<?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="#FFFFFF"
android:pathData="M54,30
A24,24 0 1,1 53.99,30
M54,36
A18,18 0 1,0 54.01,36"/>
<path
android:fillColor="#FFFFFF"
android:pathData="M54,42 L54,54 L66,54"/>
</vector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#6200EE"
android:pathData="M0,0h48v48h-48z"/>
<path
android:fillColor="#FFFFFF"
android:pathData="M24,8
A16,16 0 1,1 23.99,8
M24,12
A12,12 0 1,0 24.01,12"/>
<path
android:fillColor="#FFFFFF"
android:strokeWidth="2"
android:pathData="M24,16 L24,24 L32,24"/>
</vector>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#6200EE"
android:pathData="M0,0h48v48h-48z"/>
<path
android:fillColor="#FFFFFF"
android:pathData="M24,8
A16,16 0 1,1 23.99,8
M24,12
A12,12 0 1,0 24.01,12"/>
<path
android:fillColor="#FFFFFF"
android:strokeWidth="2"
android:pathData="M24,16 L24,24 L32,24"/>
</vector>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#6200EE"
android:pathData="M0,0h48v48h-48z"/>
<path
android:fillColor="#FFFFFF"
android:pathData="M24,8
A16,16 0 1,1 23.99,8
M24,12
A12,12 0 1,0 24.01,12"/>
<path
android:fillColor="#FFFFFF"
android:strokeWidth="2"
android:pathData="M24,16 L24,24 L32,24"/>
</vector>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#6200EE"
android:pathData="M0,0h48v48h-48z"/>
<path
android:fillColor="#FFFFFF"
android:pathData="M24,8
A16,16 0 1,1 23.99,8
M24,12
A12,12 0 1,0 24.01,12"/>
<path
android:fillColor="#FFFFFF"
android:strokeWidth="2"
android:pathData="M24,16 L24,24 L32,24"/>
</vector>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#6200EE"
android:pathData="M0,0h48v48h-48z"/>
<path
android:fillColor="#FFFFFF"
android:pathData="M24,8
A16,16 0 1,1 23.99,8
M24,12
A12,12 0 1,0 24.01,12"/>
<path
android:fillColor="#FFFFFF"
android:strokeWidth="2"
android:pathData="M24,16 L24,24 L32,24"/>
</vector>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<color name="ic_launcher_background">#6200EE</color>
</resources>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@android:color/white</item>
</style>
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@android:color/white</item>
</style>
</resources>

41
android/build.gradle Normal file
View File

@@ -0,0 +1,41 @@
def flutterSdkPath = System.env.FLUTTER_SDK ?: System.properties['flutter.sdk']
buildscript {
ext.kotlin_version = '1.8.22'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
String flutterProjectRoot = rootProject.projectDir.parentFile.toPath().normalize().toString()
File pluginsFile = new File(flutterProjectRoot, '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.text.eachLine { line ->
def pluginDef = line.split('=')
if (pluginDef.length == 2) {
def pluginName = pluginDef[0]
def pluginPath = pluginDef[1]
include ":$pluginName"
project(":$pluginName").projectDir = new File(pluginPath, 'android')
}
}
}

View File

@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx4G
android.useAndroidX=true
android.enableJetifier=true

BIN
android/gradle/wrapper/gradle-wrapper.jar vendored Executable file

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip

1
android/gradlew vendored Symbolic link
View File

@@ -0,0 +1 @@
gradlew.bat

90
android/gradlew.bat vendored Executable file
View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

5
android/gradlew.sh Normal file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
cd "$(dirname "$0")"
export ANDROID_HOME=$HOME/android-sdk
export FLUTTER_SDK=$HOME/bin/flutter
exec /home/minimax/bin/jdk-17.0.17+10/bin/java -classpath gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain "$@"

26
android/settings.gradle Normal file
View File

@@ -0,0 +1,26 @@
pluginManagement {
def flutterSdkPath = System.env.FLUTTER_SDK ?: System.properties['flutter.sdk']
assert flutterSdkPath != null, "FLUTTER_SDK not set. Run: export FLUTTER_SDK=/path/to/flutter"
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
google()
mavenCentral()
maven {
url "https://storage.googleapis.com/download.flutter.io"
}
}
}
rootProject.name = 'minimal_timer'
include(':app')