mirror of
https://github.com/IllusionMan1212/android-native-example-odin.git
synced 2026-10-24 10:12:33 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
bin
|
||||||
|
lib
|
||||||
|
obj
|
||||||
|
libmain.so
|
||||||
|
.keystore
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.NativeExample"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0">
|
||||||
|
|
||||||
|
<uses-sdk android:minSdkVersion="24" />
|
||||||
|
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:label="NativeExample"
|
||||||
|
android:hasCode="false"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:debuggable="false">
|
||||||
|
|
||||||
|
<activity android:name="android.app.NativeActivity"
|
||||||
|
android:configChanges="orientation|keyboardHidden"
|
||||||
|
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
|
||||||
|
|
||||||
|
<meta-data android:name="android.app.lib_name"
|
||||||
|
android:value="main" />
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
This is a basic Android native example written completely in Odin. It adapts and rewrites mmozeiko's [C example](https://github.com/mmozeiko/android-native-example) to work with Odin.
|
||||||
|
|
||||||
|
This requires that you write some basic bindings to the Android NDK in order to compile and run.
|
||||||
|
|
||||||
|
# Building
|
||||||
|
|
||||||
|
First you must adjust `build.sh` and `launch.sh` to have correct paths to Android SDK, Android NDK and the Jetbrains RT.
|
||||||
|
Then set `APK` variable to the desired apk package name. Use Android SDK to install build-tools and platform SDK.
|
||||||
|
|
||||||
|
Now you can use `build.sh` to build and install the app, and `launch.sh` to run the application:
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
varying vec3 color;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_FragColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
//
|
||||||
|
|
||||||
|
attribute vec2 vPosition;
|
||||||
|
attribute vec3 vColor;
|
||||||
|
varying vec3 color;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
color = vColor;
|
||||||
|
gl_Position = vec4(vPosition, 0.0, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
## TODO: handle different ABIs, currently only builds for arm64
|
||||||
|
|
||||||
|
APK=native_example.apk
|
||||||
|
ANDROID_SDK_ROOT=/home/illusion/Android/Sdk
|
||||||
|
ANDROID_NDK=$ANDROID_SDK_ROOT/ndk/26.1.10909125
|
||||||
|
ANDROID_TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64
|
||||||
|
ANDROID_JBR=/home/illusion/android-studio/jbr
|
||||||
|
BUILD_TOOLS=34.0.0
|
||||||
|
PLATFORM=34
|
||||||
|
|
||||||
|
# Create required dirs
|
||||||
|
mkdir -p lib/lib/arm64-v8a
|
||||||
|
mkdir -p bin
|
||||||
|
|
||||||
|
# Compile the glue code that comes with the NDK to an object using the aarch64 crosscompiler.
|
||||||
|
$ANDROID_TOOLCHAIN/bin/aarch64-linux-android24-clang -c $ANDROID_NDK/sources/android/native_app_glue/android_native_app_glue.c -o android_native_app_glue.o -I $ANDROID_TOOLCHAIN/sysroot/usr/include/
|
||||||
|
|
||||||
|
# Create a static lib from the glue code object
|
||||||
|
$ANDROID_TOOLCHAIN/bin/llvm-ar rcs libandroid_native_app_glue.a android_native_app_glue.o
|
||||||
|
|
||||||
|
# Compile our odin code to an object. relocation mode is set to PIC because the linker complains.
|
||||||
|
# freestanding_arm64 should probably be preferred but raylib projects need linux_arm64, otherwise the android dynamic linker fails to find our main function.
|
||||||
|
odin build jni/ -build-mode:obj -target:linux_arm64 -reloc-mode:pic || exit 1
|
||||||
|
|
||||||
|
# Link the static library with our object and create the shared library that will hold all our native code.
|
||||||
|
## We pass "-u ANativeActivity_onCreate" to the linker here because that symbol gets stripped by the linker even though it's required
|
||||||
|
## This is exactly what the official ndk-build building process does, see: https://github.com/android/ndk/issues/381
|
||||||
|
$ANDROID_TOOLCHAIN/bin/aarch64-linux-android24-clang -shared -o libmain.so jni.o libandroid_native_app_glue.a -L $ANDROID_TOOLCHAIN/sysroot/usr/lib/aarch64-linux-android/$PLATFORM/ -landroid -llog -lEGL -lGLESv3 -u ANativeActivity_onCreate
|
||||||
|
|
||||||
|
# Move the shared lib to the lib directory
|
||||||
|
cp libmain.so lib/lib/arm64-v8a
|
||||||
|
|
||||||
|
# Pack the assets of the app using a basic android jar that comes with the SDK
|
||||||
|
## If there are asset files that need to be loaded during runtime then they should be put in an `assets` directory
|
||||||
|
## and "-A assets" should be appended after the -I option.
|
||||||
|
$ANDROID_SDK_ROOT/build-tools/$BUILD_TOOLS/aapt package -f -M AndroidManifest.xml -S res -I $ANDROID_SDK_ROOT/platforms/android-$PLATFORM/android.jar -A assets -F "bin/$APK.build" lib
|
||||||
|
|
||||||
|
# Create a debug signing key to sign our application.
|
||||||
|
if [ ! -f .keystore ]; then
|
||||||
|
"$ANDROID_JBR/bin/keytool" -genkey -dname "CN=Android Debug, O=Android, C=US" -keystore .keystore -alias androiddebugkey -storepass android -keypass android -keyalg RSA -validity 30000
|
||||||
|
if [ $? -ne 0 ]; then exit 1; fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sign the jar
|
||||||
|
$ANDROID_JBR/bin/jarsigner -storepass android -keystore .keystore "bin/$APK.build" androiddebugkey > /dev/null
|
||||||
|
|
||||||
|
# Make it into an apk
|
||||||
|
$ANDROID_SDK_ROOT/build-tools/$BUILD_TOOLS/zipalign -f 4 "bin/$APK.build" "bin/$APK"
|
||||||
|
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm jni.o android_native_app_glue.o libandroid_native_app_glue.a bin/$APK.build
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup
|
||||||
+275
@@ -0,0 +1,275 @@
|
|||||||
|
package android_native_example
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
import "shared:android"
|
||||||
|
|
||||||
|
import "vendor:egl"
|
||||||
|
import gl "vendor:OpenGL"
|
||||||
|
|
||||||
|
LOG :: android.__android_log_print
|
||||||
|
|
||||||
|
engine :: struct {
|
||||||
|
app: ^android.android_app,
|
||||||
|
|
||||||
|
active: bool,
|
||||||
|
display: egl.Display,
|
||||||
|
surface: egl.Surface,
|
||||||
|
ctx: egl.Context,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
|
||||||
|
buffer: u32,
|
||||||
|
shader: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
engine_init_display :: proc(engine: ^engine) {
|
||||||
|
attribs := []i32{
|
||||||
|
egl.SURFACE_TYPE, egl.WINDOW_BIT,
|
||||||
|
egl.RENDERABLE_TYPE, egl.OPENGL_ES3_BIT,
|
||||||
|
egl.BLUE_SIZE, 8,
|
||||||
|
egl.GREEN_SIZE, 8,
|
||||||
|
egl.RED_SIZE, 8,
|
||||||
|
egl.NONE,
|
||||||
|
}
|
||||||
|
|
||||||
|
display := egl.GetDisplay(egl.DEFAULT_DISPLAY)
|
||||||
|
if display == egl.NO_DISPLAY {
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "Naterror with eglGetDisplay")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !egl.Initialize(display, nil, nil) {
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "error with eglInitialize")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: I think the major and minor versions don't matter here, we just want the gl package to load all available
|
||||||
|
// gl procs.
|
||||||
|
gl.load_up_to(
|
||||||
|
4,
|
||||||
|
6,
|
||||||
|
proc(p: rawptr, name: cstring) {(cast(^rawptr)p)^ = egl.GetProcAddress(name)}
|
||||||
|
)
|
||||||
|
|
||||||
|
config: egl.Config
|
||||||
|
numConfigs: i32
|
||||||
|
if !egl.ChooseConfig(display, raw_data(attribs), &config, 1, &numConfigs) {
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "error with eglChooseConfig")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
format: i32
|
||||||
|
if !egl.GetConfigAttrib(display, config, egl.NATIVE_VISUAL_ID, &format) {
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "error with eglGetConfigAttrib")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
android.ANativeWindow_setBuffersGeometry(engine.app.window, 0, 0, format)
|
||||||
|
|
||||||
|
surface := egl.CreateWindowSurface(display, config, cast(egl.NativeWindowType)engine.app.window, nil)
|
||||||
|
if surface == nil {
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "error with eglCreateWindowSurface")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_attrib := []i32{ egl.CONTEXT_CLIENT_VERSION, 2, egl.NONE }
|
||||||
|
ctx := egl.CreateContext(display, config, nil, raw_data(ctx_attrib))
|
||||||
|
if ctx == nil {
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "error with eglCreateContext")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !egl.MakeCurrent(display, surface, surface, ctx) {
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "error with eglMakeCurrent")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "GL_VENDOR = %s", gl.GetString(gl.VENDOR))
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "GL_RENDERER = %s", gl.GetString(gl.RENDERER))
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "GL_VERSION = %s", gl.GetString(gl.VERSION))
|
||||||
|
|
||||||
|
w: i32
|
||||||
|
h: i32
|
||||||
|
egl.QuerySurface(display, surface, egl.WIDTH, &w)
|
||||||
|
egl.QuerySurface(display, surface, egl.HEIGHT, &h)
|
||||||
|
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "initial width: %d", w)
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "initial height: %d", h)
|
||||||
|
|
||||||
|
gl.Viewport(0, 0, w, h)
|
||||||
|
|
||||||
|
engine.display = display
|
||||||
|
engine.ctx = ctx
|
||||||
|
engine.surface = surface
|
||||||
|
engine.width = w
|
||||||
|
engine.height = h
|
||||||
|
|
||||||
|
vasset := android.AAssetManager_open(engine.app.activity.assetManager, "vertex.glsl", .BUFFER)
|
||||||
|
defer android.AAsset_close(vasset)
|
||||||
|
if vasset == nil {
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "error opening vertex.glsl")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vsrc := cstring(android.AAsset_getBuffer(vasset))
|
||||||
|
vlen := i32(android.AAsset_getLength(vasset))
|
||||||
|
|
||||||
|
v := gl.CreateShader(gl.VERTEX_SHADER)
|
||||||
|
gl.ShaderSource(v, 1, &vsrc, &vlen)
|
||||||
|
gl.CompileShader(v)
|
||||||
|
|
||||||
|
fasset := android.AAssetManager_open(engine.app.activity.assetManager, "fragment.glsl", .BUFFER)
|
||||||
|
defer android.AAsset_close(fasset)
|
||||||
|
if fasset == nil {
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "error opening fragment.glsl")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fsrc := cstring(android.AAsset_getBuffer(fasset))
|
||||||
|
flen := i32(android.AAsset_getLength(fasset))
|
||||||
|
|
||||||
|
f := gl.CreateShader(gl.FRAGMENT_SHADER)
|
||||||
|
gl.ShaderSource(f, 1, &fsrc, &flen)
|
||||||
|
gl.CompileShader(f)
|
||||||
|
|
||||||
|
|
||||||
|
p := gl.CreateProgram()
|
||||||
|
gl.AttachShader(p, v)
|
||||||
|
gl.AttachShader(p, f)
|
||||||
|
|
||||||
|
gl.BindAttribLocation(p, 0, "vPosition")
|
||||||
|
gl.BindAttribLocation(p, 1, "vColor")
|
||||||
|
gl.LinkProgram(p)
|
||||||
|
|
||||||
|
gl.DeleteShader(v)
|
||||||
|
gl.DeleteShader(f)
|
||||||
|
gl.UseProgram(p)
|
||||||
|
|
||||||
|
buf := [?]f32 {
|
||||||
|
0.0, 0.5, 1, 0, 0,
|
||||||
|
-0.5, -0.5, 0, 1, 0,
|
||||||
|
0.5, -0.5, 0, 0, 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
b: u32
|
||||||
|
gl.GenBuffers(1, &b)
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, b)
|
||||||
|
gl.BufferData(gl.ARRAY_BUFFER, size_of(buf), &buf, gl.STATIC_DRAW)
|
||||||
|
|
||||||
|
engine.buffer = b
|
||||||
|
engine.shader = p
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
engine_draw_frame :: proc(engine: ^engine) {
|
||||||
|
if engine.display == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.ClearColor(0.258824, 0.258824, 0.435294, 1)
|
||||||
|
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||||
|
|
||||||
|
gl.UseProgram(engine.shader)
|
||||||
|
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, engine.buffer)
|
||||||
|
gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, (2+3)*size_of(f32), 0)
|
||||||
|
gl.VertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, (2+3)*size_of(f32), (2*size_of(f32)))
|
||||||
|
gl.EnableVertexAttribArray(0)
|
||||||
|
gl.EnableVertexAttribArray(1)
|
||||||
|
|
||||||
|
gl.DrawArrays(gl.TRIANGLES, 0, 3)
|
||||||
|
|
||||||
|
egl.SwapBuffers(engine.display, engine.surface)
|
||||||
|
}
|
||||||
|
|
||||||
|
engine_term_display :: proc(engine: ^engine) {
|
||||||
|
if engine.display != egl.NO_DISPLAY {
|
||||||
|
gl.DeleteProgram(engine.shader)
|
||||||
|
gl.DeleteBuffers(1, &engine.buffer)
|
||||||
|
|
||||||
|
egl.MakeCurrent(engine.display, egl.NO_SURFACE, egl.NO_SURFACE, egl.NO_CONTEXT)
|
||||||
|
if engine.ctx != egl.NO_CONTEXT {
|
||||||
|
egl.DestroyContext(engine.display, engine.ctx)
|
||||||
|
}
|
||||||
|
if engine.surface != egl.NO_SURFACE {
|
||||||
|
egl.DestroySurface(engine.display, engine.surface)
|
||||||
|
}
|
||||||
|
egl.Terminate(engine.display)
|
||||||
|
}
|
||||||
|
engine.active = false
|
||||||
|
engine.display = egl.NO_DISPLAY
|
||||||
|
engine.ctx = egl.NO_CONTEXT
|
||||||
|
engine.surface = egl.NO_SURFACE
|
||||||
|
}
|
||||||
|
|
||||||
|
engine_handle_input :: proc(app: ^android.android_app, event: ^android.AInputEvent) -> i32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
engine_handle_cmd :: proc(app: ^android.android_app, cmd: android.AppCmd) {
|
||||||
|
engine := cast(^engine)app.userData
|
||||||
|
|
||||||
|
#partial switch (cmd) {
|
||||||
|
case .INIT_WINDOW:
|
||||||
|
if engine.app.window != nil {
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "init window event")
|
||||||
|
engine_init_display(engine)
|
||||||
|
engine_draw_frame(engine)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .TERM_WINDOW:
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "term window event")
|
||||||
|
engine_term_display(engine)
|
||||||
|
case .WINDOW_RESIZED:
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "window resized")
|
||||||
|
h := android.ANativeWindow_getHeight(app.window)
|
||||||
|
w := android.ANativeWindow_getWidth(app.window)
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "w: %d", w)
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "h: %d", h)
|
||||||
|
gl.Viewport(0, 0, w, h)
|
||||||
|
|
||||||
|
case .GAINED_FOCUS:
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "gained focus event")
|
||||||
|
engine.active = true
|
||||||
|
|
||||||
|
case .LOST_FOCUS:
|
||||||
|
LOG(.INFO, "NativeExampleOdin", "lost focus event")
|
||||||
|
engine.active = false
|
||||||
|
engine_draw_frame(engine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@export
|
||||||
|
android_main :: proc "contextless" (state: ^android.android_app) {
|
||||||
|
context = runtime.default_context()
|
||||||
|
|
||||||
|
engine: engine
|
||||||
|
|
||||||
|
state.userData = &engine
|
||||||
|
state.onAppCmd = engine_handle_cmd
|
||||||
|
state.onInputEvent = engine_handle_input
|
||||||
|
engine.app = state
|
||||||
|
|
||||||
|
for {
|
||||||
|
events: i32
|
||||||
|
source: ^android.android_poll_source
|
||||||
|
|
||||||
|
ident := android.ALooper_pollAll(engine.active ? 0 : -1, nil, &events, cast(^rawptr)&source)
|
||||||
|
for ident >= 0 {
|
||||||
|
if source != nil {
|
||||||
|
source.process(state, source)
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.destroyRequested != 0 {
|
||||||
|
engine_term_display(&engine)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ident = android.ALooper_pollAll(engine.active ? 0 : -1, nil, &events, cast(^rawptr)&source)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (engine.active) {
|
||||||
|
engine_draw_frame(&engine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
APK=native_example.apk
|
||||||
|
ANDROID_SDK_ROOT=/home/illusion/Android/Sdk
|
||||||
|
ANDROID_NDK=$ANDROID_SDK_ROOT/ndk/26.1.10909125
|
||||||
|
ANDROID_TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64
|
||||||
|
ANDROID_JBR=/home/illusion/android-studio/jbr
|
||||||
|
BUILD_TOOLS=34.0.0
|
||||||
|
|
||||||
|
get_package_activity() {
|
||||||
|
PACKAGE=$("$ANDROID_SDK_ROOT/build-tools/$BUILD_TOOLS/aapt" dump badging "bin/$APK" | grep package | awk -F"'" '{print $2}')
|
||||||
|
ACTIVITY=$("$ANDROID_SDK_ROOT/build-tools/$BUILD_TOOLS/aapt" dump badging "bin/$APK" | grep launchable-activity | awk -F"'" '{print $2}')
|
||||||
|
}
|
||||||
|
|
||||||
|
launch() {
|
||||||
|
get_package_activity
|
||||||
|
"$ANDROID_SDK_ROOT/platform-tools/adb" shell am start -n "$PACKAGE/$ACTIVITY"
|
||||||
|
if [ $? -ne 0 ]; then exit 1; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
launch
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="260dp"
|
||||||
|
android:height="260dp"
|
||||||
|
android:viewportWidth="260"
|
||||||
|
android:viewportHeight="260">
|
||||||
|
<path
|
||||||
|
android:pathData="m260.84,130.84c0,71.8 -58.2,130 -130,130 -71.8,0 -130,-58.2 -130,-130 0,-71.8 58.2,-130 130,-130 71.8,0 130,58.2 130,130z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:centerX="29.32"
|
||||||
|
android:centerY="29.32"
|
||||||
|
android:gradientRadius="203.03"
|
||||||
|
android:type="radial">
|
||||||
|
<item android:offset="0" android:color="#FF189CFF"/>
|
||||||
|
<item android:offset="0.58" android:color="#FF138EEB"/>
|
||||||
|
<item android:offset="0.8" android:color="#FF0971BF"/>
|
||||||
|
<item android:offset="1" android:color="#FF005493"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="260dp"
|
||||||
|
android:height="260dp"
|
||||||
|
android:viewportWidth="260"
|
||||||
|
android:viewportHeight="260">
|
||||||
|
<path
|
||||||
|
android:pathData="m101.51,171.2c-1.92,-1.32 -3.44,-2.6 -3.44,-2.6L147.46,83.06c0,0 2.03,0.75 3.71,1.56 2.12,1.01 3.87,2 3.87,2 23.94,13.82 32.16,44.48 18.33,68.42 -13.82,23.94 -44.48,32.16 -68.42,18.33 0,0 -1.68,-0.96 -3.45,-2.18zM150.87,93.85 L109.13,166.15c19.95,11.52 45.5,4.67 57.02,-15.28 11.52,-19.95 4.67,-45.5 -15.28,-57.02zM88.9,158.64c-1.68,-2.43 -2.28,-3.6 -2.28,-3.6 -8.63,-14.99 -9.26,-34.05 0,-50.09 9.26,-16.04 26.08,-25.02 43.38,-25.04 0,0 2.33,-0.01 4.28,0.18 2.93,0.28 4.88,0.66 4.88,0.66L91.92,162.56c0,0 -1.12,-1.16 -3.03,-3.92zM125,88.57c-12.59,1.53 -24.33,8.75 -31.15,20.56 -6.82,11.82 -7.2,25.59 -2.23,37.26z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</vector>
|
||||||
@@ -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="@drawable/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
@@ -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="@drawable/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
Reference in New Issue
Block a user