UI: Setup Activity

This commit is contained in:
zt515 2017-12-17 00:54:32 +08:00
parent c354ee703e
commit e28b12c9fd
7 changed files with 114 additions and 265 deletions

View File

@ -133,7 +133,7 @@
android:theme="@style/AppTheme.NoActionBar.Dark" />
<activity
android:name=".ui.setup.SetupActivity"
android:exported="false"
android:exported="true"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".ui.bonus.BonusActivity"

View File

@ -1,222 +0,0 @@
package io.neoterm.component.setup;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Build;
import android.system.Os;
import android.util.Pair;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import io.neoterm.R;
import io.neoterm.backend.EmulatorDebug;
import io.neoterm.frontend.logging.NLog;
import io.neoterm.frontend.config.NeoPreference;
import io.neoterm.frontend.config.NeoTermPath;
public final class BaseFileInstaller {
public interface ResultListener {
void onResult(Exception error);
}
public static boolean needSetup() {
final File PREFIX_FILE = new File(NeoTermPath.USR_PATH);
return !PREFIX_FILE.isDirectory();
}
public static void installBaseFiles(final Activity activity, final ResultListener resultListener) {
if (!needSetup()) {
resultListener.onResult(null);
return;
}
final File PREFIX_FILE = new File(NeoTermPath.USR_PATH);
final ProgressDialog progress = makeProgressDialog(activity);
progress.setMax(100);
progress.show();
new Thread() {
@Override
public void run() {
try {
final String STAGING_PREFIX_PATH = NeoTermPath.ROOT_PATH + "/usr-staging";
final File STAGING_PREFIX_FILE = new File(STAGING_PREFIX_PATH);
if (STAGING_PREFIX_FILE.exists()) {
deleteFolder(STAGING_PREFIX_FILE);
}
int totalBytes = 0;
int totalReadBytes = 0;
final byte[] buffer = new byte[8096];
final List<Pair<String, String>> symlinks = new ArrayList<>(50);
HttpURLConnection connection = openBaseFileConnection();
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
totalBytes = connection.getContentLength();
try (ZipInputStream zipInput = new ZipInputStream(connection.getInputStream())) {
ZipEntry zipEntry;
while ((zipEntry = zipInput.getNextEntry()) != null) {
totalReadBytes += zipEntry.getCompressedSize();
final int totalReadBytesFinal = totalReadBytes;
final int totalBytesFinal = totalBytes;
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
double progressFloat = ((double) totalReadBytesFinal) / ((double) totalBytesFinal) * 100.0;
progress.setProgress((int) progressFloat);
} catch (RuntimeException ignore) {
// activity dismissed
}
}
});
if (zipEntry.getName().contains("SYMLINKS.txt")) {
BufferedReader symlinksReader = new BufferedReader(new InputStreamReader(zipInput));
String line;
while ((line = symlinksReader.readLine()) != null) {
if (line.isEmpty()) {
continue;
}
String[] parts = line.split("");
if (parts.length != 2)
throw new RuntimeException("Malformed symlink line: " + line);
String oldPath = parts[0];
String newPath = STAGING_PREFIX_PATH + "/" + parts[1];
symlinks.add(Pair.create(oldPath, newPath));
}
} else {
String zipEntryName = zipEntry.getName();
File targetFile = new File(STAGING_PREFIX_PATH, zipEntryName);
if (zipEntry.isDirectory()) {
if (!targetFile.mkdirs())
throw new RuntimeException("Failed to create directory: " + targetFile.getAbsolutePath());
} else {
try (FileOutputStream outStream = new FileOutputStream(targetFile)) {
int readBytes;
while ((readBytes = zipInput.read(buffer)) != -1) {
outStream.write(buffer, 0, readBytes);
}
}
if (zipEntryName.startsWith("bin/") || zipEntryName.startsWith("libexec") || zipEntryName.startsWith("lib/apt/methods")) {
//noinspection OctalInteger
Os.chmod(targetFile.getAbsolutePath(), 0700);
}
}
}
}
}
connection.disconnect();
if (symlinks.isEmpty())
throw new RuntimeException("No SYMLINKS.txt encountered");
for (Pair<String, String> symlink : symlinks) {
NLog.INSTANCE.e("Setup", "Linking " + symlink.first + " to " + symlink.second);
Os.symlink(symlink.first, symlink.second);
}
if (!STAGING_PREFIX_FILE.renameTo(PREFIX_FILE)) {
throw new RuntimeException("Unable to rename staging folder");
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
resultListener.onResult(null);
}
});
} catch (final Exception e) {
NLog.INSTANCE.e(EmulatorDebug.LOG_TAG, "Bootstrap error", e);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
resultListener.onResult(e);
} catch (RuntimeException e) {
// Activity already dismissed - ignore.
}
}
});
} finally {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
progress.dismiss();
} catch (RuntimeException e) {
// Activity already dismissed - ignore.
}
}
});
}
}
}.start();
}
private static HttpURLConnection openBaseFileConnection() throws IOException {
String arch = determineArchName();
String baseUrl = NeoTermPath.INSTANCE.getSERVER_BASE_URL();
// Use the same source
NeoPreference.INSTANCE.store(R.string.key_package_source, baseUrl);
return (HttpURLConnection) new URL(baseUrl + "/boot/" + arch + ".zip").openConnection();
}
private static String determineArchName() {
for (String androidArch : Build.SUPPORTED_ABIS) {
switch (androidArch) {
case "arm64-v8a":
return "aarch64";
case "armeabi-v7a":
return "arm";
case "x86_64":
return "x86_64";
// case "x86":
// return "i686";
}
}
throw new RuntimeException("Unable to determine arch from Build.SUPPORTED_ABIS = " +
Arrays.toString(Build.SUPPORTED_ABIS));
}
private static void deleteFolder(File fileOrDirectory) {
File[] children = fileOrDirectory.listFiles();
if (children != null) {
for (File child : children) {
deleteFolder(child);
}
}
if (!fileOrDirectory.delete()) {
throw new RuntimeException("Unable to delete " + (fileOrDirectory.isDirectory() ? "directory " : "file ") + fileOrDirectory.getAbsolutePath());
}
}
public static ProgressDialog makeProgressDialog(Context context) {
ProgressDialog dialog = new ProgressDialog(context);
dialog.setMessage(context.getString(R.string.installer_message));
dialog.setIndeterminate(false);
dialog.setCancelable(false);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
return dialog;
}
}

View File

@ -137,6 +137,8 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen
private fun changeSourceToUserInput(sourceManager: SourceManager) {
val editText = EditText(this)
editText.setSelectAllOnFocus(true)
AlertDialog.Builder(this)
.setTitle(R.string.pref_package_source)
.setView(editText)

View File

@ -1,41 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/white">
android:layout_margin="32dp"
android:orientation="vertical">
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:id="@+id/setup_title_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="26dp"
android:layout_marginTop="26dp"
android:gravity="center"
android:text="@string/setup_hello"
android:textColor="@color/colorAccent"
android:textSize="49sp" />
<TextView
android:id="@+id/subtitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="-7dp"
android:gravity="center"
android:text="@string/setup_info"
android:textColor="@color/colorAccent"
android:textSize="16sp" />
android:textSize="32sp" />
<TextView
android:id="@+id/setup_next"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:id="@+id/select_method_tip_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/preview_layout_margin"
android:layout_marginEnd="@dimen/preview_layout_margin"
android:text="@string/setup_next"
android:textColor="#9b9b9b"
android:textStyle="bold" />
android:layout_below="@id/setup_title_text"
android:text="@string/setup_setup_method" />
<Button
android:id="@+id/setup_next"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="@string/setup_next" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/setup_next"
android:layout_below="@id/select_method_tip_text"
android:orientation="vertical">
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checkedButton="@id/setup_method_online">
<RadioButton
android:id="@+id/setup_method_online"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/setup_online" />
<RadioButton
android:id="@+id/setup_method_local"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/setup_local" />
<RadioButton
android:id="@+id/setup_method_assets"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/setup_assets" />
</RadioGroup>
<TextView
android:id="@+id/setup_url_tip_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/setup_source_url" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/setup_source_url"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:clickable="false"
android:focusable="false"
android:hint="@string/setup_input_source_url" />
<Button
android:id="@+id/setup_source_url_select"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.2"
android:text="@string/setup_dots" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp">
<TextView
android:id="@+id/setup_log_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
</LinearLayout>
</RelativeLayout>

View File

@ -48,7 +48,7 @@
<string name="fullscreen_mode_changed">全屏模式已改变,请重启 NeoTerm</string>
<string name="permission_denied">NeoTerm 无法取得必需的权限,正在退出</string>
<string name="error">还有这种操作?</string>
<string name="use_system_shell">使用系统Shell</string>
<string name="use_system_shell">使用系统会话</string>
<string name="retry">重试</string>
<string name="source_changed">APT 源已更改,你可能需要执行 apt update 来更新</string>
<string name="package_details">软件包: %s\n版本: %s\n依赖: %s\n占用空间: %s\n描述: %s\n主页: %s</string>
@ -66,15 +66,17 @@
<string name="package_settings_desc">软件源,更新,升级</string>
<string name="install_font">安装字体</string>
<string name="install_color">安装配色方案</string>
<string name="setup_hello">发现</string>
<string name="setup_info">你好NeoTerm</string>
<string name="setup_info2">轻触以选择你的最爱</string>
<string name="setup_next">短暂的安装过程之后,\n即可开始使用NeoTerm</string>
<string name="discovery">发现</string>
<string name="setup_next">开始!</string>
<string name="setup_setup_method">选择你的配置方式</string>
<string name="setup_online">在线设置</string>
<string name="setup_local">本地 Zip 文件</string>
<string name="setup_assets">软件自带 Zip 文件</string>
<string name="setup_source_url">配置地址</string>
<string name="setup_input_source_url">输入配置地址</string>
<string name="crash_model">设备: %s</string>
<string name="crash_app">应用: %s</string>
<string name="crash_stack_trace">堆栈信息</string>
<string name="crash_tips">我们正努力让这个 Activity 永不见天日…</string>
<string name="service_status_text">%d 个会话, %d 个图形会话</string>
<string name="service_lock_acquired"> (永不休眠)</string>
<string name="service_acquire_lock">取得休眠锁</string>

View File

@ -61,15 +61,11 @@
<string name="package_settings_desc">軟體源,更新,升級</string>
<string name="install_font">安裝字體</string>
<string name="install_color">安裝色彩主題</string>
<string name="setup_hello">發現</string>
<string name="setup_info">你好NeoTerm</string>
<string name="setup_info2">輕觸以選擇你的最愛</string>
<string name="setup_next">短暫的安裝過程之後,\n即可開始使用NeoTerm</string>
<string name="discovery">發現</string>
<string name="setup_next">GO</string>
<string name="crash_model">裝置: %s</string>
<string name="crash_app">程式: %s</string>
<string name="crash_stack_trace">錯誤訊息</string>
<string name="crash_tips">我們正在努力讓這個 Activity 永不見天日…</string>
<string name="service_status_text">%d 個會話, %d 個图形會話</string>
<string name="service_lock_acquired"> (永不休眠)</string>
<string name="service_acquire_lock">開啟休眠鎖</string>

View File

@ -12,13 +12,17 @@
<string name="service_acquire_lock">Acquire Lock</string>
<string name="service_release_lock">Release Lock</string>
<string name="setup_hello">Discovery</string>
<string name="setup_info">Hello, NeoTerm</string>
<string name="setup_info2">Tap to choose your favorites</string>
<string name="setup_next">After a short installation process,\nyou can start using NeoTerm</string>
<string name="setup_next">Let\'s Go!</string>
<string name="setup_setup_method">Select your setup method</string>
<string name="setup_online">Online Setup</string>
<string name="setup_local">Local Zip File Setup</string>
<string name="setup_assets">Assets Zip File Setup</string>
<string name="setup_source_url">Source URL</string>
<string name="setup_input_source_url">Input source URL…</string>
<string name="setup_dots" translatable="false"></string>
<string name="about">About</string>
<string name="discovery">Discovery</string>
<string name="settings">Settings</string>
<string name="general_settings">General Settings</string>
<string name="general_settings_desc">Bell, Vibrate, Shell, Initial Command</string>
@ -50,7 +54,6 @@
<string name="crash_model">Model: %s</string>
<string name="crash_app">App: %s</string>
<string name="crash_tips">We are trying to deprecate this Activity…</string>
<string name="crash_stack_trace">Stack Trace</string>
<string name="installer_message">Installing</string>