Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bundle weights file in assets and deliver to external storage #1

Draft
wants to merge 1 commit into
base: lc0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions LeelaChessEngine/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
android.enableJetifier=true
android.useAndroidX=true
1 change: 1 addition & 0 deletions LeelaChessEngine/leelaChessEngine/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ android {

dependencies {
implementation project(':chessEngineSupportLibrary')
implementation 'androidx.core:core:1.2.0'
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package="com.kalab.chess.leelaengine"
android:versionCode="1"
android:versionName="0.25dev">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package com.kalab.chess.leelaengine;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.text.SpannableStringBuilder;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
Expand All @@ -21,12 +26,20 @@
import android.text.style.RelativeSizeSpan;
import android.text.style.ForegroundColorSpan;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class MainActivity extends Activity {

private class HeaderView extends LinearLayout {
public HeaderView(Context context) {
super(context);

ImageView icon = new ImageView(context);
icon.setImageResource(R.drawable.ic_launcher);
int iconSize = getRawPixels(48f);
Expand All @@ -35,7 +48,6 @@ public HeaderView(Context context) {
iconParams.height = iconSize;
iconParams.gravity = Gravity.CENTER_VERTICAL;
addView(icon, iconParams);

TextView text = new TextView(context);
text.setGravity(Gravity.CENTER);
text.setText(R.string.app_name);
Expand All @@ -61,22 +73,26 @@ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
}
}

private static String baseDirPath = "lc0";
private static String weightsDirPath = "networks";

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
createDirectories();
createLayout();
}

void createLayout() {
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
int spacing = getRawPixels(12f);
layout.setPadding(spacing, spacing, spacing, spacing);

HeaderView header = new HeaderView(this);
LinearLayout.LayoutParams headerParams = LinearLayoutParams();
headerParams.gravity = Gravity.CENTER_HORIZONTAL;
layout.addView(header, headerParams);

layout.addView(new VerticalSeparator(this));

TextView label = new TextView(this);
label.setMovementMethod(LinkMovementMethod.getInstance());
label.setText(getLabel());
Expand All @@ -85,7 +101,9 @@ public void onCreate(Bundle savedInstanceState) {
labelParams.weight = 1.0f;
labelParams.topMargin = spacing;
layout.addView(label, labelParams);

TextView notice = new TextView(this);
notice.setText(getNotice());
layout.addView(notice);
setContentView(layout);
}

Expand All @@ -104,57 +122,50 @@ String getNetworkWeights() {

private CharSequence getLabel() {
SpannableStringBuilder b = new SpannableStringBuilder();

appendLine(b, getText(R.string.includes_the_following_engines));

appendDetail(b, "Chess Engine Version", getVersionName());

appendDetail(b, "Network Weights File", getNetworkWeights());

appendLine(b, getText(R.string.to_use_them));

appendLink(b, getText(R.string.released_under), getText(R.string.source_code));

return b;
}

private CharSequence getNotice() {
if (!getPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE))
return getText(R.string.storage_permission);
else
return null;
}

private static void appendLine(SpannableStringBuilder b, CharSequence line) {
b.append(line);
b.append("\n\n");
}

private static void appendDetail(SpannableStringBuilder b, String title, String subtitle) {
b.append(" ");

int nameStart = b.length();
b.append(title);
b.setSpan(new StyleSpan(Typeface.BOLD), nameStart, b.length(), 0);

b.append("\n ");

int subtitleStart = b.length();
b.append(subtitle);

b.setSpan(new RelativeSizeSpan(0.8f), subtitleStart, b.length(), 0);
b.setSpan(new ForegroundColorSpan(0xff808080), subtitleStart, b.length(), 0);

b.append("\n\n");
}

private void appendLink(SpannableStringBuilder b, CharSequence line, final CharSequence link) {
b.append(line);
b.append(" (");

int downloadLinkStart = b.length();

b.append("source code");
b.setSpan(new ClickableSpan() {
@Override
public void onClick(View widget) {
launchUri(link);
}
}, downloadLinkStart, b.length(), 0);

b.append(").");
}

Expand All @@ -173,4 +184,86 @@ private int getRawPixels(float dp) {
private LinearLayout.LayoutParams LinearLayoutParams() {
return new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
}

/** Create directory structure on external storage. */
private void createDirectories() {
if (!getPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE))
return;
copyAssets(baseDirPath, weightsDirPath);
}

boolean getPermission(String permission) {
if (ContextCompat.checkSelfPermission(this, permission) ==
PackageManager.PERMISSION_GRANTED) {
return true;
} else {
ActivityCompat.requestPermissions(this, new String[]{permission}, 0);
return false;
}
}

@Override
public void onRequestPermissionsResult(int code, String[] permissions, int[] results) {
for (int i = 0; i < permissions.length; ++i) {
if (results[i] == PackageManager.PERMISSION_GRANTED) {
if (permissions[i].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
createDirectories();
}
}
}
}

/** Copy assets to external storage. */
private void copyAssets(String base, String dir) {
AssetManager assetManager = getAssets();
String[] files = null;
try {
files = assetManager.list(dir);
} catch (IOException e) {
Log.e("tag", "Failed to get asset file list.", e);
}
if (files != null) {
for (String filename : files) {
InputStream in = null;
OutputStream out = null;
try {
File outDir = new File(Environment.getExternalStorageDirectory() + File.separator + base + File.separator + dir);
outDir.mkdirs();
File outFile = new File(outDir, filename);

AssetFileDescriptor asset = assetManager.openFd(dir + File.separator + filename);
if (asset.getLength() != outFile.length()) {
in = asset.createInputStream();
out = new FileOutputStream(outFile);
copyFile(in, out);
}
} catch (IOException e) {
Log.e("tag", "Failed to copy asset file: " + filename, e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// NOOP
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
// NOOP
}
}
}
}
}
}

private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while((read = in.read(buffer)) != -1){
out.write(buffer, 0, read);
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Leela Chess Zero Engine</string>
<string name="app_name">Leela Chess Zero</string>
<string name="network_weights">591226</string>
<string name="includes_the_following_engines">This package provides the Leela Chess Zero engine.</string>
<string name="to_use_them">To use it, you need a chess application compatible with the Open Exchange protocol.</string>
<string name="released_under">Leela Chess Zero is released under the terms of the GNU General Public License v3</string>
<string name="build">Build</string>
<string name="source_code">https://github.com/LeelaChessZero/lc0</string>
<string name="source_code">https://github.com/LeelaChessZero</string>
<string name="storage_permission">To function properly, the engine needs to access its network weights file from the local storage. Please give storage permission to this application.</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
<engine
name="Leela Chess Zero"
filename="liblc0.so"
target="armeabi-v7a|arm64-v8a" />
target="armeabi-v7a|arm64-v8a|x86" />

</enginelist>