Skip to content

Commit

Permalink
working build
Browse files Browse the repository at this point in the history
  • Loading branch information
hazae41 committed May 9, 2019
0 parents commit e8f8331
Show file tree
Hide file tree
Showing 40 changed files with 684 additions and 0 deletions.
33 changes: 33 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
language: android
jdk: oraclejdk8

android:
components:
- tools
- platform-tools
- build-tools-28.0.3
- android-28

before_install:
- wget http://services.gradle.org/distributions/gradle-5.4.1-bin.zip
- unzip -qq gradle-5.4.1-bin.zip
- export GRADLE_HOME=$PWD/gradle-5.4.1
- export PATH=$GRADLE_HOME/bin:$PATH
- gradle -v

script:
- gradle build

after_success:
- sh publish.sh

deploy:
provider: releases
api_key:
secure: $GITHUB_API_KEY
file: "app/build/outputs/apk/debug/app-debug.apk"
file_glob: "true"
skip_cleanup: true
on:
branch: dev
repo: hazae41/sweet-ipfs
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[![Build Status](https://travis-ci.com/hazae41/sweet-ipfs.svg?branch=dev)](https://travis-ci.com/hazae41/sweet-ipfs)

<h3 align="center">
<img src="https://i.imgur.com/YM1XneZ.png"/>
<br>
:earth_africa: Full IPFS node for Android :earth_africa:
<br><br><br>
<a href="https://play.google.com/store/apps/details?id=fr.rhaz.ipfs.sweet"><img src="http://ligi.de/img/play_badge.png"/></a>
</h3>


[![](https://i.imgur.com/dhxagpE.png)](https://steemit.com/ipfs/@hazae41/sweet-ipfs-full-ipfs-node-for-android#upvote_button)

<hr>

### What is IPFS? 🌌

The InterPlanetary File System "is a peer-to-peer (p2p) filesharing system that aims to fundamentally change the way information is distributed across & beyond the globe. IPFS consists of several innovations in communication protocols and distributed systems that have been combined to produce a file system like no other."

"It attempts to address the deficiencies of the client-server model and HTTP web through a novel p2p file sharing system."

Source: https://hackernoon.com/a-beginners-guide-to-ipfs-20673fedd3f

Official website of IPFS: https://ipfs.io/
35 changes: 35 additions & 0 deletions app/binaries.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
tasks.whenTaskAdded { task ->
if(task.name in ['assembleRelease', 'assembleDebug'])
task.dependsOn downloadIPFSBinaries
}

task downloadIPFSBinaries {
doLast {
def version = "v0.4.20"
def archs = ["arm", "arm64", "386", "amd64"]
def assets = "./src/main/assets"
def getFile = { arch -> "go-ipfs_${version}_linux-${arch}.tar.gz" }
def getUrl = { arch -> "https://dist.ipfs.io/go-ipfs/${version}/go-ipfs_${version}_linux-${arch}.tar.gz" }

archs.each { arch ->
println "Downloading ${arch} binary..."
def archive = new File(buildDir, getFile(arch))
download {
src getUrl(arch)
dest archive
overwrite true
}
copy {
from(tarTree(resources.gzip(archive))) {
include "go-ipfs/ipfs"
eachFile { file ->
file.relativePath = new RelativePath(true, file.relativePath.segments.drop(1))
}
includeEmptyDirs = false
}
into assets
rename "ipfs", arch
}
}
}
}
39 changes: 39 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
plugins {
id "de.undercouch.download" version "3.4.3"
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply from: 'binaries.gradle'

android {
compileSdkVersion 28
defaultConfig {
applicationId "fr.rhaz.ipfs.sweet"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:+"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.1'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.code.gson:gson:+'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
21 changes: 21 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -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
35 changes: 35 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
package="fr.rhaz.ipfs.sweet">

<dist:module dist:instant="true"/>

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-feature android:name="android.hardware.touchscreen"
android:required="false"/>

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:theme="@style/AppTheme">
<service
android:name=".DaemonService"
android:enabled="true"
android:exported="true">
</service>
<activity android:name=".WebActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>

</manifest>
151 changes: 151 additions & 0 deletions app/src/main/java/fr/rhaz/ipfs/sweet/DaemonService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package fr.rhaz.ipfs.sweet

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.NotificationManager.IMPORTANCE_MIN
import android.app.Service
import android.content.Intent
import android.graphics.Color.parseColor
import android.os.Build
import android.os.Build.SUPPORTED_ABIS
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.google.gson.GsonBuilder
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import java.io.FileReader
import java.lang.Runtime.getRuntime

class DaemonService : Service(), CoroutineScope {
private val job = SupervisorJob()
override val coroutineContext get() = Dispatchers.IO + job

override fun onBind(intent: Intent) = null

var daemon: Process? = null

val store get() = getExternalFilesDir(null)!!["ipfs"]
val bin get() = filesDir["ipfsbin"]

val config get() = JsonParser().parse(FileReader(store["config"])).asJsonObject

fun config(consumer: JsonObject.() -> Unit) {
val config = config.apply(consumer)
val data = GsonBuilder().setPrettyPrinting().create().toJson(config)
store["config"].writeBytes(data.toByteArray())
}


fun exec(cmd: String) = getRuntime().exec(
"${bin.absolutePath} $cmd",
arrayOf("IPFS_PATH=${store.absolutePath}")
)

override fun onCreate() {
super.onCreate()

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
NotificationChannel("sweetipfs", "Sweet IPFS", IMPORTANCE_MIN).apply {
description = "Sweet IPFS"
getSystemService(NotificationManager::class.java)
.createNotificationChannel(this)
}

launch {
if (!bin.exists()) install().join()
start().join()
startForeground(1, notification.build())
}
}

fun install() = launch {
var type: String? = null
for (abi in SUPPORTED_ABIS) {
if (type != null) break
type = when (abi) {
"arm64-v8a" -> "arm64"
"x86_64" -> "amd64"
"armeabi", "armeabi-v7a" -> "arm"
"x86", "386" -> "386"
else -> null
}
}

if (type == null) throw Exception("Unsupported ABI")

bin.delete()
bin.createNewFile()

val input = assets.open(type)
val output = bin.outputStream()

try {
input.copyTo(output)
} finally {
input.close(); output.close()
}

bin.setExecutable(true)
}

fun start() = launch {
val init = exec("init")
init.read()
init.waitFor()

config {
val headers = obj("API").obj("HTTPHeaders")
val origins = headers.array("Access-Control-Allow-Origin")
val webui = json("https://webui.ipfs.io")
if (webui !in origins) origins.add(webui)
}

val daemon = exec("daemon")
this@DaemonService.daemon = daemon
daemon.read()
}

fun Process.read() {
launch {
inputStream.bufferedReader().forEachLine { println(it) }
}
launch {
errorStream.bufferedReader().forEachLine { println(it) }
}
}

val notification
get() = NotificationCompat.Builder(this, "sweetipfs").apply {
setOngoing(true)
color = parseColor("#69c4cd")
setSmallIcon(R.drawable.ic_cloud)
setShowWhen(false)
setContentTitle("Sweet IPFS")
setContentText("IPFS is running")

val open = pendingActivity<WebActivity>()
setContentIntent(open)

val exit = pendingService(intent<DaemonService>().action("exit"))
addAction(R.drawable.ic_cloud, "exit", exit)
}

override fun onStartCommand(i: Intent?, f: Int, id: Int) = START_STICKY.also {
super.onStartCommand(i, f, id)
when (i?.action) {
"start" -> {
}
"exit" -> stopSelf()
}
}

override fun onDestroy() = super.onDestroy().also {
daemon?.destroy()
NotificationManagerCompat.from(this).cancel(1)
}

}
Loading

0 comments on commit e8f8331

Please sign in to comment.