diff --git a/app/build.gradle b/app/build.gradle index d27ed67e..4a9a0dad 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,6 +10,7 @@ android { targetSdkVersion 29 versionCode 257 versionName "2.5.1" + vectorDrawables.useSupportLibrary true } buildTypes { release { @@ -33,4 +34,6 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.browser:browser:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'com.squareup.okhttp3:okhttp:4.3.1' + implementation 'androidx.preference:preference:1.1.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ead33afa..19a178a1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,13 +22,13 @@ android:usesCleartextTraffic="true" tools:ignore="GoogleAppIndexingWarning"> + android:targetActivity=".activity.MainActivity"> @@ -37,56 +37,56 @@ + android:parentActivityName=".activity.MainActivity"> + android:value=".activity.MainActivity" /> + android:value=".activity.MainActivity" /> + android:parentActivityName=".activity.MainActivity"> + android:value=".activity.MainActivity" /> + android:parentActivityName=".activity.MainActivity"> + android:value=".activity.MainActivity" /> + android:parentActivityName=".activity.PropertiesActivity"> + android:value=".activity.PropertiesActivity" /> + android:parentActivityName=".activity.MainActivity"> + android:value=".activity.MainActivity" /> - + @@ -95,7 +95,7 @@ @@ -103,12 +103,12 @@ diff --git a/app/src/main/java/ru/meefik/linuxdeploy/AppCompatPreferenceActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/AppCompatPreferenceActivity.java deleted file mode 100644 index 47873ed0..00000000 --- a/app/src/main/java/ru/meefik/linuxdeploy/AppCompatPreferenceActivity.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ru.meefik.linuxdeploy; - -import android.content.res.Configuration; -import android.os.Bundle; -import android.preference.PreferenceActivity; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.LayoutRes; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatDelegate; -import androidx.core.app.NavUtils; - -/** - * A {@link PreferenceActivity} which implements and proxies the necessary calls - * to be used with AppCompat. - *

- * This technique can be used with an {@link android.app.Activity} class, not just - * {@link PreferenceActivity}. - */ -public abstract class AppCompatPreferenceActivity extends PreferenceActivity { - private AppCompatDelegate mDelegate; - - @Override - protected void onCreate(Bundle savedInstanceState) { - getDelegate().installViewFactory(); - getDelegate().onCreate(savedInstanceState); - super.onCreate(savedInstanceState); - } - - @Override - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - getDelegate().onPostCreate(savedInstanceState); - } - - public ActionBar getSupportActionBar() { - return getDelegate().getSupportActionBar(); - } - - @Override - public MenuInflater getMenuInflater() { - return getDelegate().getMenuInflater(); - } - - @Override - public void setContentView(@LayoutRes int layoutResID) { - getDelegate().setContentView(layoutResID); - } - - @Override - public void setContentView(View view) { - getDelegate().setContentView(view); - } - - @Override - public void setContentView(View view, ViewGroup.LayoutParams params) { - getDelegate().setContentView(view, params); - } - - @Override - public void addContentView(View view, ViewGroup.LayoutParams params) { - getDelegate().addContentView(view, params); - } - - @Override - protected void onPostResume() { - super.onPostResume(); - getDelegate().onPostResume(); - } - - @Override - protected void onTitleChanged(CharSequence title, int color) { - super.onTitleChanged(title, color); - getDelegate().setTitle(title); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - getDelegate().onConfigurationChanged(newConfig); - } - - @Override - protected void onStop() { - super.onStop(); - getDelegate().onStop(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - getDelegate().onDestroy(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - NavUtils.navigateUpFromSameTask(this); - return true; - } - return super.onOptionsItemSelected(item); - } - - @Override - public void invalidateOptionsMenu() { - getDelegate().invalidateOptionsMenu(); - } - - private AppCompatDelegate getDelegate() { - if (mDelegate == null) { - mDelegate = AppCompatDelegate.create(this, null); - } - return mDelegate; - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/meefik/linuxdeploy/EnvUtils.java b/app/src/main/java/ru/meefik/linuxdeploy/EnvUtils.java index f9349901..4aebff63 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/EnvUtils.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/EnvUtils.java @@ -6,7 +6,6 @@ import java.io.BufferedReader; import java.io.BufferedWriter; -import java.io.Closeable; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; @@ -19,22 +18,7 @@ import java.util.ArrayList; import java.util.List; -class EnvUtils { - - /** - * Closeable helper - * - * @param c closable object - */ - private static void close(Closeable c) { - if (c != null) { - try { - c.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } +public class EnvUtils { /** * Extract file to env directory @@ -47,28 +31,22 @@ private static void close(Closeable c) { */ private static boolean extractFile(Context c, String target, String rootAsset, String path) { AssetManager assetManager = c.getAssets(); - InputStream in = null; - OutputStream out = null; - boolean result = true; - try { - in = assetManager.open(rootAsset + path); + + try (InputStream in = assetManager.open(rootAsset + path)) { File fname = new File(target + path); fname.delete(); - out = new FileOutputStream(fname); - byte[] buffer = new byte[1024]; - int read; - while ((read = in.read(buffer)) != -1) { - out.write(buffer, 0, read); + try (OutputStream out = new FileOutputStream(fname)) { + byte[] buffer = new byte[1024]; + int read; + while ((read = in.read(buffer)) != -1) { + out.write(buffer, 0, read); + } + out.flush(); } - out.flush(); + return true; } catch (IOException e) { - e.printStackTrace(); - result = false; - } finally { - close(in); - close(out); + return false; } - return result; } /** @@ -148,45 +126,20 @@ private static void setPermissions(File path, Boolean executable) { * @return true if success */ private static boolean isRooted() { - boolean result = false; - OutputStream stdin = null; - InputStream stdout = null; - int n = 0; try { Process process = Runtime.getRuntime().exec("su"); - stdin = process.getOutputStream(); - stdout = process.getInputStream(); - - DataOutputStream os = null; - try { - os = new DataOutputStream(stdin); - os.writeBytes("ls /data\n"); - os.writeBytes("exit\n"); - os.flush(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - close(os); - } + try (DataOutputStream stdin = new DataOutputStream(process.getOutputStream()); + BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()))) { - BufferedReader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(stdout)); - while (reader.readLine() != null) { - n++; - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - close(reader); + stdin.writeBytes("ls /data\n"); + stdin.writeBytes("exit\n"); + stdin.flush(); + + return stdout.readLine() != null; } } catch (IOException e) { - e.printStackTrace(); - } finally { - close(stdout); - close(stdin); + return false; } - return n > 0; } /** @@ -196,19 +149,13 @@ private static boolean isRooted() { * @return true if success */ private static boolean setVersion(Context c) { - boolean result = false; String f = PrefStore.getEnvDir(c) + "/version"; - BufferedWriter bw = null; - try { - bw = new BufferedWriter(new FileWriter(f)); - bw.write(PrefStore.getVersion(c)); - result = true; + try (BufferedWriter bw = new BufferedWriter(new FileWriter(f))) { + bw.write(PrefStore.getVersion()); + return true; } catch (IOException e) { - e.printStackTrace(); - } finally { - close(bw); + return false; } - return result; } /** @@ -217,21 +164,16 @@ private static boolean setVersion(Context c) { * @param c context * @return true if success */ - static boolean isLatestVersion(Context c) { + public static boolean isLatestVersion(Context c) { File f = new File(PrefStore.getEnvDir(c) + "/version"); if (!f.exists()) return false; - boolean result = false; - BufferedReader br = null; - try { - br = new BufferedReader(new FileReader(f)); + + try (BufferedReader br = new BufferedReader(new FileReader(f))) { String line = br.readLine(); - if (PrefStore.getVersion(c).equals(line)) result = true; + return PrefStore.getVersion().equals(line); } catch (IOException e) { - e.printStackTrace(); - } finally { - close(br); + return false; } - return result; } /** @@ -246,14 +188,12 @@ public static boolean exec(final Context c, final String shell, final List protocol = new ArrayList<>(); private static char lastChar = '\n'; @@ -66,7 +68,7 @@ private static synchronized void appendMessage(Context c, final String msg) { * @param c context * @return true if success */ - static boolean clear(Context c) { + public static boolean clear(Context c) { protocol.clear(); File logFile = new File(PrefStore.getLogFile(c)); return logFile.delete(); @@ -77,14 +79,14 @@ static boolean clear(Context c) { * * @return size */ - static int size() { + public static int size() { return protocol.size(); } /** * Show log on main activity */ - static void show() { + public static void show() { MainActivity.showLog(get()); } @@ -129,10 +131,8 @@ private static void close(Closeable c) { * @param stream stream */ static void log(Context c, InputStream stream) { - BufferedReader reader = null; FileWriter writer = null; - try { - reader = new BufferedReader(new InputStreamReader(stream)); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))){ if (PrefStore.isLogger(c)) { writer = new FileWriter(PrefStore.getLogFile(c)); } @@ -147,7 +147,6 @@ static void log(Context c, InputStream stream) { e.printStackTrace(); } finally { close(writer); - close(reader); close(stream); } } diff --git a/app/src/main/java/ru/meefik/linuxdeploy/MountsActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/MountsActivity.java deleted file mode 100644 index e516aeff..00000000 --- a/app/src/main/java/ru/meefik/linuxdeploy/MountsActivity.java +++ /dev/null @@ -1,193 +0,0 @@ -package ru.meefik.linuxdeploy; - -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ListView; -import android.widget.TextView; - -import java.util.ArrayList; -import java.util.List; - -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; - -public class MountsActivity extends AppCompatActivity { - - private List listItems = new ArrayList<>(); - private ArrayAdapter adapter; - - private void addDialog() { - LayoutInflater layoutInflater = LayoutInflater.from(this); - View view = layoutInflater.inflate(R.layout.properties_mounts, null); - final EditText inputSrc = view.findViewById(R.id.editTextSrc); - final EditText inputTarget = view.findViewById(R.id.editTextTarget); - new AlertDialog.Builder(this) - .setTitle(R.string.new_mount_title) - .setView(view) - .setPositiveButton(android.R.string.ok, - (dialog, whichButton) -> { - String text = ""; - String src = inputSrc.getText().toString() - .replaceAll("[ :]", "_"); - String target = inputTarget.getText().toString() - .replaceAll("[ :]", "_"); - if (src.length() > 0) { - text = src; - if (target.length() > 0) { - text = text + ":" + target; - } - } - if (text.length() > 0) { - listItems.add(text); - adapter.notifyDataSetChanged(); - } - }).setNegativeButton(android.R.string.cancel, - (dialog, whichButton) -> dialog.cancel()).show(); - } - - private void editDialog(final int position) { - LayoutInflater layoutInflater = LayoutInflater.from(this); - View view = layoutInflater.inflate(R.layout.properties_mounts, null); - final EditText inputSrc = view.findViewById(R.id.editTextSrc); - final EditText inputTarget = view.findViewById(R.id.editTextTarget); - if (position >= 0 && position < listItems.size()) { - String text = listItems.get(position); - final String[] arr = text.split(":", 2); - try { - inputSrc.setText(arr[0]); - inputSrc.setSelection(arr[0].length()); - - inputTarget.setText(arr[1]); - inputTarget.setSelection(arr[1].length()); - } catch (IndexOutOfBoundsException ignored) { - } - - new AlertDialog.Builder(this) - .setTitle(R.string.edit_mount_title) - .setView(view) - .setPositiveButton(android.R.string.ok, - (dialog, whichButton) -> { - String text1 = ""; - String src = inputSrc.getText().toString() - .replaceAll("[ :]", "_"); - String target = inputTarget.getText().toString() - .replaceAll("[ :]", "_"); - if (src.length() > 0) { - text1 = src; - if (target.length() > 0) { - text1 = text1 + ":" + target; - } - } - if (text1.length() > 0) { - listItems.set(position, text1); - adapter.notifyDataSetChanged(); - } - }).setNegativeButton(android.R.string.cancel, - (dialog, whichButton) -> dialog.cancel()).show(); - } - } - - private void deleteDialog(final int position) { - if (position >= 0 && position < listItems.size()) { - new AlertDialog.Builder(this) - .setTitle(R.string.confirm_mount_discard_title) - .setMessage(R.string.confirm_mount_discard_message) - .setIcon(android.R.drawable.ic_dialog_alert) - .setCancelable(false) - .setPositiveButton(android.R.string.yes, - (dialog, whichButton) -> { - listItems.remove(position); - adapter.notifyDataSetChanged(); - }).setNegativeButton(android.R.string.no, - (dialog, whichButton) -> dialog.cancel()).show(); - } - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - PrefStore.setLocale(this); - setContentView(R.layout.activity_mounts); - - // ListView Adapter - ListView listView = findViewById(R.id.mountsView); - adapter = new ArrayAdapter(this, R.layout.mounts_row, R.id.mount_point, listItems) { - @Override - public View getView(final int position, View convertView, final ViewGroup parent) { - View view = super.getView(position, convertView, parent); - TextView tv = view.findViewById(R.id.mount_point); - Button btn = view.findViewById(R.id.delete_mount); - - String item = getItem(position); - tv.setText(item); - - tv.setOnClickListener(v -> { - ((ListView) parent).performItemClick(v, position, 0); // Let the event be handled in onItemClick() - }); - - btn.setOnClickListener(v -> { - ((ListView) parent).performItemClick(v, position, 0); // Let the event be handled in onItemClick() - }); - - return view; - } - }; - listView.setAdapter(adapter); - - // Click listener - listView.setOnItemClickListener((parent, view, position, id) -> { - long viewId = view.getId(); - if (viewId == R.id.delete_mount) deleteDialog(position); - else editDialog(position); - }); - } - - @Override - public void setTheme(int resId) { - super.setTheme(PrefStore.getTheme(this)); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - PrefStore.setLocale(this); - getMenuInflater().inflate(R.menu.activity_mounts, menu); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_add: - addDialog(); - break; - } - return false; - } - - @Override - public void onResume() { - super.onResume(); - - String titleMsg = getString(R.string.title_activity_mounts) + ": " - + PrefStore.getProfileName(this); - setTitle(titleMsg); - - listItems.addAll(PrefStore.getMountsList(this)); - } - - @Override - public void onPause() { - super.onPause(); - - PrefStore.setMountsList(this, listItems); - } -} diff --git a/app/src/main/java/ru/meefik/linuxdeploy/NetworkReceiver.java b/app/src/main/java/ru/meefik/linuxdeploy/NetworkReceiver.java deleted file mode 100644 index e557663a..00000000 --- a/app/src/main/java/ru/meefik/linuxdeploy/NetworkReceiver.java +++ /dev/null @@ -1,27 +0,0 @@ -package ru.meefik.linuxdeploy; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; - -public class NetworkReceiver extends BroadcastReceiver { - - @Override - public void onReceive(final Context context, Intent intent) { - if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - ConnectivityManager cm = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - - NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); - boolean isConnected = false; - if (activeNetwork != null) isConnected = activeNetwork.isConnected(); - if (isConnected) { - EnvUtils.execService(context, "start", "core/net"); - } else { - EnvUtils.execService(context, "stop", "core/net"); - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/meefik/linuxdeploy/ParamUtils.java b/app/src/main/java/ru/meefik/linuxdeploy/ParamUtils.java index 3cec7084..eaecae40 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/ParamUtils.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/ParamUtils.java @@ -5,7 +5,6 @@ import java.io.BufferedReader; import java.io.BufferedWriter; -import java.io.Closeable; import java.io.File; import java.io.FileReader; import java.io.FileWriter; @@ -25,27 +24,11 @@ class ParamUtils { this.params = Arrays.asList(params); } - /** - * Closeable helper - * - * @param c closable object - */ - private static void close(Closeable c) { - if (c != null) { - try { - c.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - private static Map readConf(File confFile) { TreeMap map = new TreeMap<>(); - BufferedReader br = null; - String line; - try { - br = new BufferedReader(new FileReader(confFile)); + + try (BufferedReader br = new BufferedReader(new FileReader(confFile))) { + String line; while ((line = br.readLine()) != null) { if (!line.startsWith("#") && !line.isEmpty()) { String[] pair = line.split("="); @@ -54,32 +37,25 @@ private static Map readConf(File confFile) { map.put(key, value.replaceAll("\"", "")); } } - } catch (Exception e) { - e.printStackTrace(); - } finally { - close(br); + } catch (IOException e) { + // Error! } + return map; } private static boolean writeConf(Map map, File confFile) { - Boolean result = false; - BufferedWriter bw = null; - try { - bw = new BufferedWriter(new FileWriter(confFile)); + try (BufferedWriter bw = new BufferedWriter(new FileWriter(confFile))) { for (Map.Entry entry : map.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); bw.write(key + "=\"" + value + "\""); bw.newLine(); } - result = true; - } catch (Exception e) { - e.printStackTrace(); - } finally { - close(bw); + return true; + } catch (IOException e) { + return false; } - return result; } public String fixOutputParam(Context c, String key, String value) { @@ -169,7 +145,8 @@ boolean restore(Context c, File f) { void clear(Context c, boolean all) { SharedPreferences pref = c.getSharedPreferences(this.name, Context.MODE_PRIVATE); SharedPreferences.Editor prefEditor = pref.edit(); - if (all) prefEditor.clear(); + if (all) + prefEditor.clear(); else { for (Map.Entry entry : pref.getAll().entrySet()) { String key = entry.getKey(); diff --git a/app/src/main/java/ru/meefik/linuxdeploy/PrefStore.java b/app/src/main/java/ru/meefik/linuxdeploy/PrefStore.java index f068b3a6..b43c13d7 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/PrefStore.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/PrefStore.java @@ -11,6 +11,9 @@ import android.view.Display; import android.view.WindowManager; +import androidx.core.app.NotificationCompat; +import androidx.core.app.TaskStackBuilder; + import java.io.File; import java.net.Inet4Address; import java.net.InetAddress; @@ -22,8 +25,7 @@ import java.util.List; import java.util.Locale; -import androidx.core.app.NotificationCompat; -import androidx.core.app.TaskStackBuilder; +import ru.meefik.linuxdeploy.activity.MainActivity; import static ru.meefik.linuxdeploy.App.SERVICE_CHANNEL_ID; @@ -36,10 +38,9 @@ public class PrefStore { /** * Get application version * - * @param c context * @return version, format versionName-versionCode */ - static String getVersion(Context c) { + public static String getVersion() { return BuildConfig.VERSION_NAME + "-" + BuildConfig.VERSION_CODE; } @@ -49,7 +50,7 @@ static String getVersion(Context c) { * @param c context * @return path, e.g. /data/data/package/files */ - static String getEnvDir(Context c) { + public static String getEnvDir(Context c) { String envDir = SETTINGS.get(c, "env_dir"); if (envDir.isEmpty()) { envDir = c.getFilesDir().getAbsolutePath(); @@ -103,7 +104,7 @@ static String getWebDir(Context c) { * @param c context * @return true if success */ - static boolean dumpSettings(Context c) { + public static boolean dumpSettings(Context c) { return SETTINGS.dump(c, getSettingsConfFile(c)); } @@ -113,7 +114,7 @@ static boolean dumpSettings(Context c) { * @param c context * @return true if success */ - static boolean restoreSettings(Context c) { + public static boolean restoreSettings(Context c) { return SETTINGS.restore(c, getSettingsConfFile(c)); } @@ -123,7 +124,7 @@ static boolean restoreSettings(Context c) { * @param c context * @return true if success */ - static boolean dumpProperties(Context c) { + public static boolean dumpProperties(Context c) { return PROPERTIES.dump(c, getPropertiesConfFile(c)); } @@ -133,7 +134,7 @@ static boolean dumpProperties(Context c) { * @param c context * @return true if success */ - static boolean restoreProperties(Context c) { + public static boolean restoreProperties(Context c) { PROPERTIES.clear(c, true); return PROPERTIES.restore(c, getPropertiesConfFile(c)); } @@ -143,7 +144,7 @@ static boolean restoreProperties(Context c) { * * @return name */ - static String getSettingsSharedName() { + public static String getSettingsSharedName() { return SettingsStore.name; } @@ -152,7 +153,7 @@ static String getSettingsSharedName() { * * @return name */ - static String getPropertiesSharedName() { + public static String getPropertiesSharedName() { return PropertiesStore.name; } @@ -195,7 +196,7 @@ private static String getLanguage(Context c) { * @param c context * @return resource id */ - static int getTheme(Context c) { + public static int getTheme(Context c) { String theme = SETTINGS.get(c, "theme"); int themeId = R.style.DarkTheme; switch (theme) { @@ -215,7 +216,7 @@ static int getTheme(Context c) { * @param c context * @return font size */ - static int getFontSize(Context c) { + public static int getFontSize(Context c) { int fontSizeInt; String fontSize = SETTINGS.get(c, "fontsize"); try { @@ -293,7 +294,7 @@ static Boolean isLogger(Context c) { * @param c context * @return path */ - static String getLogFile(Context c) { + public static String getLogFile(Context c) { String logFile = SETTINGS.get(c, "logfile"); if (!logFile.contains("/")) { String storageDir = Environment.getExternalStorageDirectory().getAbsolutePath(); @@ -308,7 +309,7 @@ static String getLogFile(Context c) { * @param c context * @return true if enabled */ - static Boolean isScreenLock(Context c) { + public static Boolean isScreenLock(Context c) { return SETTINGS.get(c, "screenlock").equals("true"); } @@ -318,7 +319,7 @@ static Boolean isScreenLock(Context c) { * @param c context * @return true if enabled */ - static Boolean isWifiLock(Context c) { + public static Boolean isWifiLock(Context c) { return SETTINGS.get(c, "wifilock").equals("true"); } @@ -328,7 +329,7 @@ static Boolean isWifiLock(Context c) { * @param c context * @return true if enabled */ - static Boolean isWakeLock(Context c) { + public static Boolean isWakeLock(Context c) { return SETTINGS.get(c, "wakelock").equals("true"); } @@ -338,7 +339,7 @@ static Boolean isWakeLock(Context c) { * @param c context * @return true if enabled */ - static Boolean isAutostart(Context c) { + public static Boolean isAutostart(Context c) { return SETTINGS.get(c, "autostart").equals("true"); } @@ -348,7 +349,7 @@ static Boolean isAutostart(Context c) { * @param c context * @return Auto start delay in seconds */ - static Integer getAutostartDelay(Context c) { + public static Integer getAutostartDelay(Context c) { try { return Integer.parseInt(SETTINGS.get(c, "autostart_delay")); } catch (Exception e) { @@ -362,7 +363,7 @@ static Integer getAutostartDelay(Context c) { * @param c context * @return true if enabled */ - static Boolean isNetTrack(Context c) { + public static Boolean isNetTrack(Context c) { return SETTINGS.get(c, "nettrack").equals("true"); } @@ -372,7 +373,7 @@ static Boolean isNetTrack(Context c) { * @param c context * @return true if enabled */ - static Boolean isPowerTrack(Context c) { + public static Boolean isPowerTrack(Context c) { return SETTINGS.get(c, "powertrack").equals("true"); } @@ -392,7 +393,7 @@ private static Boolean isNotification(Context c) { * @param c context * @return true if enabled */ - static Boolean isStealth(Context c) { + public static Boolean isStealth(Context c) { return SETTINGS.get(c, "stealth").equals("true"); } @@ -431,7 +432,7 @@ static String getShell(Context c) { * @param c context * @return url */ - static String getRepositoryUrl(Context c) { + public static String getRepositoryUrl(Context c) { return SETTINGS.get(c, "repository_url"); } @@ -440,7 +441,7 @@ static String getRepositoryUrl(Context c) { * * @param c context */ - static void setRepositoryUrl(Context c, String url) { + public static void setRepositoryUrl(Context c, String url) { SETTINGS.set(c, "repository_url", url); } @@ -490,7 +491,7 @@ static Boolean isHttp(Context c) { * @param c context * @return port */ - static String getHttpPort(Context c) { + public static String getHttpPort(Context c) { return SETTINGS.get(c, "http_port"); } @@ -500,7 +501,7 @@ static String getHttpPort(Context c) { * @param c context * @return authentication string, e.g. /:user:password (for crypt password use httpd -m password) */ - static String getHttpConf(Context c) { + public static String getHttpConf(Context c) { String auth = SETTINGS.get(c, "http_conf"); if (auth.isEmpty()) auth = "/:android:" + generatePassword(); return auth; @@ -512,7 +513,7 @@ static String getHttpConf(Context c) { * @param c context * @return true if enabled */ - static boolean isXserver(Context c) { + public static boolean isXserver(Context c) { return PROPERTIES.get(c, "is_gui").equals("true") && PROPERTIES.get(c, "graphics").equals("x11"); } @@ -523,7 +524,7 @@ static boolean isXserver(Context c) { * @param c context * @return true if enabled */ - static boolean isFramebuffer(Context c) { + public static boolean isFramebuffer(Context c) { return PROPERTIES.get(c, "is_gui").equals("true") && PROPERTIES.get(c, "graphics").equals("fb"); } @@ -534,7 +535,7 @@ static boolean isFramebuffer(Context c) { * @param c context * @return true if enabled */ - static boolean isXsdl(Context c) { + public static boolean isXsdl(Context c) { return PROPERTIES.get(c, "x11_sdl").equals("true"); } @@ -544,7 +545,7 @@ static boolean isXsdl(Context c) { * @param c context * @return delay in ms */ - static int getXsdlDelay(Context c) { + public static int getXsdlDelay(Context c) { int deplayInt; String delay = PROPERTIES.get(c, "x11_sdl_delay"); try { @@ -583,7 +584,7 @@ static File getPropertiesConfFile(Context c) { * @param c context * @return profile */ - static String getProfileName(Context c) { + public static String getProfileName(Context c) { return SETTINGS.get(c, "profile"); } @@ -592,7 +593,7 @@ static String getProfileName(Context c) { * * @param c context */ - static void changeProfile(Context c, String profile) { + public static void changeProfile(Context c, String profile) { SETTINGS.set(c, "profile", profile); dumpSettings(c); File confFile = getPropertiesConfFile(c); @@ -607,7 +608,7 @@ static void changeProfile(Context c, String profile) { * * @param c context */ - static void setLocale(Context c) { + public static void setLocale(Context c) { String language = getLanguage(c); Locale locale = new Locale(language); Locale.setDefault(locale); @@ -622,7 +623,7 @@ static void setLocale(Context c) { * @param c context * @return list of mount points */ - static List getMountsList(Context c) { + public static List getMountsList(Context c) { String str = PROPERTIES.get(c, "mounts"); List list = new ArrayList<>(); if (!str.isEmpty()) Collections.addAll(list, str.split(" ")); @@ -635,7 +636,7 @@ static List getMountsList(Context c) { * @param c context * @param list list of mount points */ - static void setMountsList(Context c, List list) { + public static void setMountsList(Context c, List list) { PROPERTIES.set(c, "mounts", TextUtils.join(" ", list)); } @@ -644,7 +645,7 @@ static void setMountsList(Context c, List list) { * * @return plain password */ - static String generatePassword() { + public static String generatePassword() { return Long.toHexString(Double.doubleToLongBits(Math.random())).substring(8); } @@ -654,7 +655,7 @@ static String generatePassword() { * @param arch unformated architecture * @return arm, arm_64, x86, x86_64 */ - static String getArch(String arch) { + public static String getArch(String arch) { String march = "unknown"; if (arch.length() > 0) { char a = arch.toLowerCase().charAt(0); @@ -679,7 +680,7 @@ static String getArch(String arch) { * * @return arm, arm_64, x86, x86_64 */ - static String getArch() { + public static String getArch() { return getArch(System.getProperty("os.arch")); } @@ -689,14 +690,12 @@ static String getArch() { * @param c context * @return screen width */ - static Integer getScreenWidth(Context c) { - int width; + static int getScreenWidth(Context c) { WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); Point size = new Point(); display.getSize(size); - width = size.x; - return width; + return size.x; } /** @@ -705,14 +704,12 @@ static Integer getScreenWidth(Context c) { * @param c context * @return screen height */ - static Integer getScreenHeight(Context c) { - int height; + static int getScreenHeight(Context c) { WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); Point size = new Point(); display.getSize(size); - height = size.y; - return height; + return size.y; } /** @@ -720,7 +717,7 @@ static Integer getScreenHeight(Context c) { * * @return ip address */ - static String getLocalIpAddress() { + public static String getLocalIpAddress() { String ip = "127.0.0.1"; try { for (Enumeration en = NetworkInterface @@ -748,7 +745,7 @@ static String getLocalIpAddress() { * @param resourceType resource type * @return resource id */ - static int getResourceId(Context c, String resourceName, String resourceType) { + public static int getResourceId(Context c, String resourceName, String resourceType) { try { return c.getResources().getIdentifier(resourceName, resourceType, c.getPackageName()); } catch (Exception e) { @@ -762,12 +759,12 @@ static int getResourceId(Context c, String resourceName, String resourceType) { * @param context context * @param intent intent */ - static void showNotification(Context context, Intent intent) { - NotificationManager mNotificationManager = (NotificationManager) context + public static void showNotification(Context context, Intent intent) { + NotificationManager notificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); if (isNotification(context)) { setLocale(context); - NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context, SERVICE_CHANNEL_ID) + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, SERVICE_CHANNEL_ID) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle(context.getString(R.string.app_name)) .setContentText(context.getString(R.string.notification_current_profile) @@ -778,7 +775,7 @@ static void showNotification(Context context, Intent intent) { stealthReceive.setAction("ru.meefik.linuxdeploy.BROADCAST_ACTION"); stealthReceive.putExtra("show", true); PendingIntent pendingIntentStealth = PendingIntent.getBroadcast(context, 2, stealthReceive, PendingIntent.FLAG_UPDATE_CURRENT); - mBuilder.setContentIntent(pendingIntentStealth); + notificationBuilder.setContentIntent(pendingIntentStealth); } else { Intent resultIntent = intent; if (resultIntent == null) resultIntent = new Intent(context, MainActivity.class); @@ -786,27 +783,25 @@ static void showNotification(Context context, Intent intent) { stackBuilder.addParentStack(MainActivity.class); stackBuilder.addNextIntent(resultIntent); PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(1, PendingIntent.FLAG_UPDATE_CURRENT); - mBuilder.setContentIntent(resultPendingIntent); + notificationBuilder.setContentIntent(resultPendingIntent); Intent startReceive = new Intent(); startReceive.setAction("ru.meefik.linuxdeploy.BROADCAST_ACTION"); startReceive.putExtra("start", true); PendingIntent pendingIntentStart = PendingIntent.getBroadcast(context, 3, startReceive, PendingIntent.FLAG_UPDATE_CURRENT); - int startIcon = SETTINGS.get(context, "theme").equals("dark") ? R.drawable.ic_action_start_dark : R.drawable.ic_action_start_light; - mBuilder.addAction(startIcon, context.getString(R.string.menu_start), pendingIntentStart); + notificationBuilder.addAction(R.drawable.ic_play_arrow_24dp, context.getString(R.string.menu_start), pendingIntentStart); Intent stopReceive = new Intent(); stopReceive.setAction("ru.meefik.linuxdeploy.BROADCAST_ACTION"); stopReceive.putExtra("stop", true); PendingIntent pendingIntentStop = PendingIntent.getBroadcast(context, 4, stopReceive, PendingIntent.FLAG_UPDATE_CURRENT); - int stopIcon = SETTINGS.get(context, "theme").equals("dark") ? R.drawable.ic_action_stop_dark : R.drawable.ic_action_stop_light; - mBuilder.addAction(stopIcon, context.getString(R.string.menu_stop), pendingIntentStop); + notificationBuilder.addAction(R.drawable.ic_stop_24dp, context.getString(R.string.menu_stop), pendingIntentStop); } - mBuilder.setOngoing(true); - mBuilder.setWhen(0); - mNotificationManager.notify(NOTIFY_ID, mBuilder.build()); + notificationBuilder.setOngoing(true); + notificationBuilder.setWhen(0); + notificationManager.notify(NOTIFY_ID, notificationBuilder.build()); } else { - mNotificationManager.cancel(NOTIFY_ID); + notificationManager.cancel(NOTIFY_ID); } } @@ -815,9 +810,9 @@ static void showNotification(Context context, Intent intent) { * * @param context context */ - static void hideNotification(Context context) { - NotificationManager mNotificationManager = (NotificationManager) context + public static void hideNotification(Context context) { + NotificationManager notificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); - mNotificationManager.cancel(NOTIFY_ID); + notificationManager.cancel(NOTIFY_ID); } } diff --git a/app/src/main/java/ru/meefik/linuxdeploy/RemoveEnvTask.java b/app/src/main/java/ru/meefik/linuxdeploy/RemoveEnvTask.java index bb7dcafa..d212b717 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/RemoveEnvTask.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/RemoveEnvTask.java @@ -6,12 +6,12 @@ import java.lang.ref.WeakReference; -class RemoveEnvTask extends AsyncTask { +public class RemoveEnvTask extends AsyncTask { private ProgressDialog dialog; private WeakReference contextWeakReference; - RemoveEnvTask(Context c) { + public RemoveEnvTask(Context c) { contextWeakReference = new WeakReference<>(c); } diff --git a/app/src/main/java/ru/meefik/linuxdeploy/RepositoryActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/RepositoryActivity.java deleted file mode 100644 index 24980324..00000000 --- a/app/src/main/java/ru/meefik/linuxdeploy/RepositoryActivity.java +++ /dev/null @@ -1,389 +0,0 @@ -package ru.meefik.linuxdeploy; - -import android.app.ProgressDialog; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.Toast; - -import java.io.BufferedReader; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.lang.ref.WeakReference; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLConnection; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.zip.GZIPInputStream; - -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; - -public class RepositoryActivity extends AppCompatActivity { - - private List> profiles = new ArrayList<>(); - private ArrayAdapter> adapter; - - private boolean isDonated() { - return getPackageManager().checkSignatures(getPackageName(), "ru.meefik.donate") - == PackageManager.SIGNATURE_MATCH; - } - - private void importDialog(final Map profile) { - final String name = profile.get("PROFILE"); - final String message = getString(R.string.repository_import_message, - profile.get("DESC"), - profile.get("SIZE")); - AlertDialog.Builder dialog = new AlertDialog.Builder(this) - .setTitle(name) - .setMessage(message) - .setCancelable(false) - .setNegativeButton(android.R.string.no, - (dialog13, whichButton) -> dialog13.cancel()); - if (!isDonated()) { - dialog.setPositiveButton(R.string.repository_purchase_button, - (dialog12, whichButton) -> startActivity(new Intent(Intent.ACTION_VIEW, - Uri.parse("https://play.google.com/store/apps/details?id=ru.meefik.donate")))); - } else { - dialog.setPositiveButton(R.string.repository_import_button, - (dialog1, whichButton) -> importProfile(name)); - } - dialog.show(); - } - - private void changeUrlDialog() { - final EditText input = new EditText(this); - input.setText(PrefStore.getRepositoryUrl(this)); - input.setSelection(input.getText().length()); - new AlertDialog.Builder(this) - .setTitle(R.string.repository_change_url_title) - .setView(input) - .setPositiveButton(android.R.string.ok, - (dialog, whichButton) -> { - String text = input.getText().toString(); - if (text.isEmpty()) text = getString(R.string.repository_url); - PrefStore.setRepositoryUrl(getApplicationContext(), text); - retrieveIndex(); - }).setNegativeButton(android.R.string.cancel, - (dialog, whichButton) -> dialog.cancel()).show(); - } - - private void retrieveIndex() { - String url = PrefStore.getRepositoryUrl(this); - new RetrieveIndexTask(this).execute(url); - } - - private void importProfile(String name) { - String url = PrefStore.getRepositoryUrl(this); - new ImportProfileTask(this).execute(url, name); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - PrefStore.setLocale(this); - setContentView(R.layout.activity_repository); - - // ListView Adapter - ListView listView = findViewById(R.id.repositoryView); - adapter = new ArrayAdapter>(this, - R.layout.repository_row, R.id.repo_entry_title, profiles) { - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View view = super.getView(position, convertView, parent); - TextView title = view.findViewById(R.id.repo_entry_title); - TextView subTitle = view.findViewById(R.id.repo_entry_subtitle); - ImageView icon = view.findViewById(R.id.repo_entry_icon); - String name = profiles.get(position).get("PROFILE"); - String desc = profiles.get(position).get("DESC"); - String type = profiles.get(position).get("TYPE"); - int iconRes = R.raw.linux; - if (type != null) { - switch (type) { - case "alpine": - iconRes = R.raw.alpine; - break; - case "archlinux": - iconRes = R.raw.archlinux; - break; - case "centos": - iconRes = R.raw.centos; - break; - case "debian": - iconRes = R.raw.debian; - break; - case "fedora": - iconRes = R.raw.fedora; - break; - case "kali": - iconRes = R.raw.kali; - break; - case "slackware": - iconRes = R.raw.slackware; - break; - case "ubuntu": - iconRes = R.raw.ubuntu; - break; - } - } - InputStream imageStream = view.getResources().openRawResource(iconRes); - Bitmap bitmap = BitmapFactory.decodeStream(imageStream); - icon.setImageBitmap(bitmap); - title.setText(name); - if (desc != null && !desc.isEmpty()) subTitle.setText(desc); - else subTitle.setText(getString(R.string.repository_default_description)); - return view; - } - }; - listView.setAdapter(adapter); - - // Click listener - listView.setOnItemClickListener((parent, view, position, id) -> { - Map profile = (Map) parent.getItemAtPosition(position); - importDialog(profile); - }); - - // Load list - retrieveIndex(); - } - - @Override - public void setTheme(int resId) { - super.setTheme(PrefStore.getTheme(this)); - } - - @Override - public void onResume() { - super.onResume(); - setTitle(R.string.title_activity_repository); - } - - @Override - public void onPause() { - super.onPause(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - PrefStore.setLocale(this); - getMenuInflater().inflate(R.menu.activity_repository, menu); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_refresh: - retrieveIndex(); - break; - case R.id.menu_change_url: - changeUrlDialog(); - break; - } - return false; - } - - static class RetrieveIndexTask extends AsyncTask { - - private ProgressDialog dialog; - private WeakReference contextWeakReference; - - RetrieveIndexTask(RepositoryActivity context) { - this.contextWeakReference = new WeakReference<>(context); - } - - @Override - protected void onPreExecute() { - RepositoryActivity context = contextWeakReference.get(); - - if (context != null) { - dialog = new ProgressDialog(context); - dialog.setMessage(context.getString(R.string.loading_message)); - dialog.show(); - context.profiles.clear(); - } - } - - @Override - protected Boolean doInBackground(String... params) { - // params comes from the execute() call: params[0] is the url. - try { - downloadUrl(params[0]); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - return true; - } - - @Override - protected void onPostExecute(Boolean success) { - RepositoryActivity context = contextWeakReference.get(); - - if (context != null) { - if (dialog.isShowing()) { - dialog.dismiss(); - } - context.adapter.notifyDataSetChanged(); - if (!success) { - Toast.makeText(context, R.string.toast_loading_error, Toast.LENGTH_SHORT).show(); - } - } - } - - private void downloadUrl(String url) throws IOException { - BufferedReader reader = null; - try { - url = url + "/index.gz"; - HttpURLConnection conn; - boolean redirect; - do { - redirect = false; - conn = (HttpURLConnection) (new URL(url)).openConnection(); - // normally, 3xx is redirect - int status = conn.getResponseCode(); - if (status != HttpURLConnection.HTTP_OK) { - if (status == HttpURLConnection.HTTP_MOVED_TEMP - || status == HttpURLConnection.HTTP_MOVED_PERM - || status == HttpURLConnection.HTTP_SEE_OTHER) - redirect = true; - } - if (redirect) { - url = conn.getHeaderField("Location"); - } - } while (redirect); - conn.connect(); - reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(conn.getInputStream()))); - String line; - Map map = new HashMap<>(); - while ((line = reader.readLine()) != null) { - if (line.isEmpty()) { - if (!map.isEmpty()) { - RepositoryActivity context = contextWeakReference.get(); - if (context != null) { - context.profiles.add(map); - } - } - map = new HashMap<>(); - continue; - } - if (!line.startsWith("#")) { - String[] pair = line.split("="); - String key = pair[0]; - String value = pair[1]; - map.put(key, value); - } - } - } finally { - if (reader != null) reader.close(); - } - } - } - - static class ImportProfileTask extends AsyncTask { - - private ProgressDialog dialog; - private WeakReference contextWeakReference; - private String profile; - - ImportProfileTask(RepositoryActivity context) { - this.contextWeakReference = new WeakReference<>(context); - } - - @Override - protected void onPreExecute() { - RepositoryActivity context = contextWeakReference.get(); - if (context != null) { - dialog = new ProgressDialog(context); - dialog.setMessage(context.getString(R.string.loading_message)); - dialog.show(); - } - } - - @Override - protected Boolean doInBackground(String... params) { - profile = params[1]; - try { - downloadUrlAndImport(params[0], params[1]); - } catch (Exception e) { - return false; - } - return true; - } - - @Override - protected void onPostExecute(Boolean success) { - RepositoryActivity context = contextWeakReference.get(); - if (context != null) { - if (dialog.isShowing()) { - dialog.dismiss(); - } - if (success) { - PrefStore.changeProfile(context, profile); - context.finish(); - } else { - Toast.makeText(context, R.string.toast_loading_error, Toast.LENGTH_SHORT).show(); - } - } - } - - private void downloadUrlAndImport(String url, String profile) throws IOException { - RepositoryActivity context = contextWeakReference.get(); - if (context != null) { - String conf = PrefStore.getEnvDir(context) + "/config/" + profile + ".conf"; - InputStream in = null; - OutputStream out = null; - try { - url = url + "/config/" + profile + ".conf"; - HttpURLConnection conn; - boolean redirect; - do { - redirect = false; - conn = (HttpURLConnection) (new URL(url)).openConnection(); - // normally, 3xx is redirect - int status = conn.getResponseCode(); - if (status != HttpURLConnection.HTTP_OK) { - if (status == HttpURLConnection.HTTP_MOVED_TEMP - || status == HttpURLConnection.HTTP_MOVED_PERM - || status == HttpURLConnection.HTTP_SEE_OTHER) - redirect = true; - } - if (redirect) { - url = conn.getHeaderField("Location"); - } - } while (redirect); - conn.connect(); - in = conn.getInputStream(); - out = new FileOutputStream(conf); - byte[] buffer = new byte[1024]; - int read; - while ((read = in.read(buffer)) != -1) { - out.write(buffer, 0, read); - } - } finally { - if (in != null) in.close(); - if (out != null) out.close(); - } - } - } - } -} diff --git a/app/src/main/java/ru/meefik/linuxdeploy/UpdateEnvTask.java b/app/src/main/java/ru/meefik/linuxdeploy/UpdateEnvTask.java index 02171c53..a617f3dc 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/UpdateEnvTask.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/UpdateEnvTask.java @@ -7,12 +7,12 @@ import java.lang.ref.WeakReference; -class UpdateEnvTask extends AsyncTask { +public class UpdateEnvTask extends AsyncTask { private ProgressDialog dialog; private WeakReference contextWeakReference; - UpdateEnvTask(Context c) { + public UpdateEnvTask(Context c) { contextWeakReference = new WeakReference<>(c); } diff --git a/app/src/main/java/ru/meefik/linuxdeploy/AboutActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/activity/AboutActivity.java similarity index 87% rename from app/src/main/java/ru/meefik/linuxdeploy/AboutActivity.java rename to app/src/main/java/ru/meefik/linuxdeploy/activity/AboutActivity.java index 9f3846d6..c5c1395f 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/AboutActivity.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/activity/AboutActivity.java @@ -1,4 +1,4 @@ -package ru.meefik.linuxdeploy; +package ru.meefik.linuxdeploy.activity; import android.os.Bundle; import android.text.method.LinkMovementMethod; @@ -6,6 +6,9 @@ import androidx.appcompat.app.AppCompatActivity; +import ru.meefik.linuxdeploy.PrefStore; +import ru.meefik.linuxdeploy.R; + public class AboutActivity extends AppCompatActivity { @Override @@ -16,7 +19,7 @@ public void onCreate(Bundle savedInstanceState) { TextView atv = findViewById(R.id.aboutTextView); atv.setMovementMethod(LinkMovementMethod.getInstance()); TextView vtv = findViewById(R.id.versionView); - vtv.setText(getString(R.string.app_version, PrefStore.getVersion(this))); + vtv.setText(getString(R.string.app_version, PrefStore.getVersion())); } @Override diff --git a/app/src/main/java/ru/meefik/linuxdeploy/FullscreenActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/activity/FullscreenActivity.java similarity index 94% rename from app/src/main/java/ru/meefik/linuxdeploy/FullscreenActivity.java rename to app/src/main/java/ru/meefik/linuxdeploy/activity/FullscreenActivity.java index 09fcdc6c..2ea01c55 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/FullscreenActivity.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/activity/FullscreenActivity.java @@ -1,4 +1,4 @@ -package ru.meefik.linuxdeploy; +package ru.meefik.linuxdeploy.activity; import android.content.pm.ActivityInfo; import android.os.Bundle; @@ -8,6 +8,9 @@ import androidx.appcompat.app.AppCompatActivity; +import ru.meefik.linuxdeploy.PrefStore; +import ru.meefik.linuxdeploy.R; + public class FullscreenActivity extends AppCompatActivity { @Override diff --git a/app/src/main/java/ru/meefik/linuxdeploy/MainActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/activity/MainActivity.java similarity index 90% rename from app/src/main/java/ru/meefik/linuxdeploy/MainActivity.java rename to app/src/main/java/ru/meefik/linuxdeploy/activity/MainActivity.java index b0f143c2..71c5840f 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/MainActivity.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/activity/MainActivity.java @@ -1,4 +1,4 @@ -package ru.meefik.linuxdeploy; +package ru.meefik.linuxdeploy.activity; import android.Manifest; import android.content.Intent; @@ -10,6 +10,7 @@ import android.net.Uri; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.WifiLock; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; @@ -24,8 +25,6 @@ import android.widget.TextView; import android.widget.Toast; -import com.google.android.material.navigation.NavigationView; - import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; @@ -37,6 +36,16 @@ import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; +import com.google.android.material.navigation.NavigationView; + +import ru.meefik.linuxdeploy.EnvUtils; +import ru.meefik.linuxdeploy.Logger; +import ru.meefik.linuxdeploy.PrefStore; +import ru.meefik.linuxdeploy.R; +import ru.meefik.linuxdeploy.UpdateEnvTask; +import ru.meefik.linuxdeploy.receiver.NetworkReceiver; +import ru.meefik.linuxdeploy.receiver.PowerReceiver; + public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { @@ -46,6 +55,8 @@ public class MainActivity extends AppCompatActivity implements private static WifiLock wifiLock; private static PowerManager.WakeLock wakeLock; + private DrawerLayout drawer; + private NetworkReceiver networkReceiver; private PowerReceiver powerReceiver; @@ -92,7 +103,7 @@ public void onCreate(Bundle savedInstanceState) { actionBar.setDisplayHomeAsUpEnabled(true); } - DrawerLayout drawer = findViewById(R.id.drawer_layout); + drawer = findViewById(R.id.drawer_layout); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawer.addDrawerListener(toggle); @@ -108,7 +119,12 @@ public void onCreate(Bundle savedInstanceState) { // WiFi lock init WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(android.content.Context.WIFI_SERVICE); - wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, getPackageName()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + // WIFI_MODE_FULL has been deprecated since API level 29 and will have no impact! + wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, getPackageName()); + } else { + wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, getPackageName()); + } // Wake lock PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); @@ -116,9 +132,9 @@ public void onCreate(Bundle savedInstanceState) { // Network receiver if (PrefStore.isNetTrack(this)) { - IntentFilter filter = new IntentFilter(); - filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - registerReceiver(getNetworkReceiver(), filter); + IntentFilter filter = new IntentFilter(); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + registerReceiver(getNetworkReceiver(), filter); } else if (networkReceiver != null) { unregisterReceiver(networkReceiver); } @@ -188,7 +204,6 @@ public boolean onOptionsItemSelected(MenuItem item) { clearLog(); break; case android.R.id.home: - DrawerLayout drawer = findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { @@ -198,12 +213,12 @@ public boolean onOptionsItemSelected(MenuItem item) { default: return super.onOptionsItemSelected(item); } - return false; + + return true; } @Override public void onBackPressed() { - DrawerLayout drawer = findViewById(R.id.drawer_layout); if (drawer != null && drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { @@ -251,7 +266,7 @@ public boolean onNavigationItemSelected(MenuItem item) { finish(); break; } - DrawerLayout drawer = findViewById(R.id.drawer_layout); + drawer.closeDrawer(GravityCompat.START); return true; } @@ -289,7 +304,7 @@ public void onResume() { // Wake lock if (PrefStore.isWakeLock(this)) { - if (!wakeLock.isHeld()) wakeLock.acquire(60*60*1000L /*60 minutes*/); + if (!wakeLock.isHeld()) wakeLock.acquire(60 * 60 * 1000L /*60 minutes*/); } else { if (wakeLock.isHeld()) wakeLock.release(); } @@ -333,8 +348,10 @@ public void containerStart(View view) { } else { EnvUtils.execService(getBaseContext(), "start", "-m"); } - }).setNegativeButton(android.R.string.no, - (dialog, id) -> dialog.cancel()).show(); + }) + .setNegativeButton(android.R.string.no, + (dialog, id) -> dialog.cancel()) + .show(); } /** @@ -348,8 +365,10 @@ public void containerStop(View view) { .setIcon(android.R.drawable.ic_dialog_alert) .setCancelable(false) .setPositiveButton(android.R.string.yes, - (dialog, id) -> EnvUtils.execService(getBaseContext(), "stop", "-u")).setNegativeButton(android.R.string.no, - (dialog, id) -> dialog.cancel()).show(); + (dialog, id) -> EnvUtils.execService(getBaseContext(), "stop", "-u")) + .setNegativeButton(android.R.string.no, + (dialog, id) -> dialog.cancel()) + .show(); } /** @@ -374,7 +393,8 @@ private void containerDeploy() { .setPositiveButton(android.R.string.yes, (dialog, id) -> EnvUtils.execService(getApplicationContext(), "deploy", null)) .setNegativeButton(android.R.string.no, - (dialog, id) -> dialog.cancel()).show(); + (dialog, id) -> dialog.cancel()) + .show(); } /** @@ -388,7 +408,8 @@ private void containerConfigure() { .setPositiveButton(android.R.string.yes, (dialog, id) -> EnvUtils.execService(getBaseContext(), "deploy", "-m -n bootstrap")) .setNegativeButton(android.R.string.no, - (dialog, id) -> dialog.cancel()).show(); + (dialog, id) -> dialog.cancel()) + .show(); } /** @@ -405,7 +426,8 @@ private void containerExport() { .setPositiveButton(android.R.string.yes, (dialog, id) -> EnvUtils.execService(getBaseContext(), "export", input.getText().toString())) .setNegativeButton(android.R.string.no, - (dialog, id) -> dialog.cancel()).show(); + (dialog, id) -> dialog.cancel()) + .show(); } /** diff --git a/app/src/main/java/ru/meefik/linuxdeploy/activity/MountsActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/activity/MountsActivity.java new file mode 100644 index 00000000..7d1d7c2f --- /dev/null +++ b/app/src/main/java/ru/meefik/linuxdeploy/activity/MountsActivity.java @@ -0,0 +1,146 @@ +package ru.meefik.linuxdeploy.activity; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.EditText; + +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import ru.meefik.linuxdeploy.PrefStore; +import ru.meefik.linuxdeploy.R; +import ru.meefik.linuxdeploy.adapter.MountAdapter; +import ru.meefik.linuxdeploy.model.Mount; + +public class MountsActivity extends AppCompatActivity { + + private MountAdapter adapter; + + private void addDialog() { + View view = LayoutInflater.from(this).inflate(R.layout.properties_mounts, null); + EditText inputSrc = view.findViewById(R.id.editTextSrc); + EditText inputTarget = view.findViewById(R.id.editTextTarget); + + new AlertDialog.Builder(this) + .setTitle(R.string.new_mount_title) + .setView(view) + .setPositiveButton(android.R.string.ok, + (dialog, whichButton) -> { + String src = inputSrc.getText().toString() + .replaceAll("[ :]", "_"); + String target = inputTarget.getText().toString() + .replaceAll("[ :]", "_"); + if (!src.isEmpty()) { + adapter.addMount(new Mount(src, target)); + } + }) + .setNegativeButton(android.R.string.cancel, + (dialog, whichButton) -> dialog.cancel()).show(); + } + + private void editDialog(Mount mount) { + View view = LayoutInflater.from(this).inflate(R.layout.properties_mounts, null); + EditText inputSrc = view.findViewById(R.id.editTextSrc); + EditText inputTarget = view.findViewById(R.id.editTextTarget); + + inputSrc.setText(mount.getSource()); + inputSrc.setSelection(mount.getSource().length()); + + inputTarget.setText(mount.getTarget()); + inputTarget.setSelection(mount.getTarget().length()); + + new AlertDialog.Builder(this) + .setTitle(R.string.edit_mount_title) + .setView(view) + .setPositiveButton(android.R.string.ok, + (dialog, whichButton) -> { + String src = inputSrc.getText().toString() + .replaceAll("[ :]", "_"); + String target = inputTarget.getText().toString() + .replaceAll("[ :]", "_"); + if (!src.isEmpty()) { + mount.setSource(src); + mount.setTarget(target); + adapter.notifyDataSetChanged(); + } + }) + .setNegativeButton(android.R.string.cancel, + (dialog, whichButton) -> dialog.cancel()) + .show(); + } + + private void deleteDialog(Mount mount) { + new AlertDialog.Builder(this) + .setTitle(R.string.confirm_mount_discard_title) + .setMessage(R.string.confirm_mount_discard_message) + .setIcon(R.drawable.ic_warning_24dp) + .setPositiveButton(android.R.string.yes, + (dialog, whichButton) -> adapter.removeMount(mount)) + .setNegativeButton(android.R.string.no, + (dialog, whichButton) -> dialog.cancel()) + .show(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + PrefStore.setLocale(this); + setContentView(R.layout.activity_mounts); + + // RecyclerView Adapter + RecyclerView recyclerView = findViewById(R.id.recycler_view); + adapter = new MountAdapter(); + adapter.setOnItemClickListener(this::editDialog); + adapter.setOnItemDeleteListener(this::deleteDialog); + + recyclerView.setAdapter(adapter); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); + } + + @Override + public void setTheme(int resId) { + super.setTheme(PrefStore.getTheme(this)); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + PrefStore.setLocale(this); + getMenuInflater().inflate(R.menu.activity_mounts, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.menu_add) { + addDialog(); + return true; + } + + return false; + } + + @Override + public void onResume() { + super.onResume(); + + String titleMsg = getString(R.string.title_activity_mounts) + ": " + + PrefStore.getProfileName(this); + setTitle(titleMsg); + + adapter.setMounts(PrefStore.getMountsList(this)); + } + + @Override + public void onPause() { + super.onPause(); + + PrefStore.setMountsList(this, adapter.getMounts()); + } +} diff --git a/app/src/main/java/ru/meefik/linuxdeploy/ProfilesActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/activity/ProfilesActivity.java similarity index 84% rename from app/src/main/java/ru/meefik/linuxdeploy/ProfilesActivity.java rename to app/src/main/java/ru/meefik/linuxdeploy/activity/ProfilesActivity.java index 2087533d..29c03344 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/ProfilesActivity.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/activity/ProfilesActivity.java @@ -1,9 +1,9 @@ -package ru.meefik.linuxdeploy; +package ru.meefik.linuxdeploy.activity; import android.content.Context; -import android.content.DialogInterface; import android.os.Bundle; import android.view.GestureDetector; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; @@ -13,13 +13,16 @@ import android.widget.EditText; import android.widget.ListView; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; + import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; +import ru.meefik.linuxdeploy.PrefStore; +import ru.meefik.linuxdeploy.R; public class ProfilesActivity extends AppCompatActivity implements OnTouchListener { @@ -63,17 +66,19 @@ public static boolean removeConf(Context c, String name) { public static List getProfiles(Context c) { List profiles = new ArrayList<>(); File confDir = new File(PrefStore.getEnvDir(c) + "/config"); - File[] listOfFiles = confDir.listFiles(); - if (listOfFiles != null) { - for (File listOfFile : listOfFiles) { - if (listOfFile.isFile()) { - String filename = listOfFile.getName(); + File[] profileFiles = confDir.listFiles(); + + if (profileFiles != null) { + for (File profileFile : profileFiles) { + if (profileFile.isFile()) { + String filename = profileFile.getName(); int index = filename.lastIndexOf('.'); if (index != -1) filename = filename.substring(0, index); profiles.add(filename); } } } + return profiles; } @@ -84,45 +89,51 @@ public static List getProfiles(Context c) { * @return position */ private int getPosition(String key) { - int pos = 0; - for (String item : listItems) { - if (item.equals(key)) return pos; - pos++; + for (int i = 0; i < listItems.size(); i++) { + if (listItems.get(i).equals(key)) + return i; } + return -1; } private void addDialog() { - final EditText input = new EditText(this); + View view = LayoutInflater.from(this).inflate(R.layout.edit_text_dialog, null); + EditText input = view.findViewById(R.id.edit_text); + new AlertDialog.Builder(this) .setTitle(R.string.new_profile_title) - .setView(input) + .setView(view) .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> { String text = input.getText().toString(); - if (text.length() > 0) { + if (!text.isEmpty()) { listItems.add(text.replaceAll("[^A-Za-z0-9_\\-]", "_")); adapter.notifyDataSetChanged(); } }) .setNegativeButton(android.R.string.cancel, - (dialog, whichButton) -> dialog.cancel()).show(); + (dialog, whichButton) -> dialog.cancel()) + .show(); } private void editDialog() { - final EditText input = new EditText(this); - final int pos = listView.getCheckedItemPosition(); + int pos = listView.getCheckedItemPosition(); if (pos >= 0 && pos < listItems.size()) { - final String profileOld = listItems.get(pos); + String profileOld = listItems.get(pos); + + View view = LayoutInflater.from(this).inflate(R.layout.edit_text_dialog, null); + EditText input = view.findViewById(R.id.edit_text); input.setText(profileOld); input.setSelection(input.getText().length()); + new AlertDialog.Builder(this) .setTitle(R.string.edit_profile_title) - .setView(input) + .setView(view) .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> { String text = input.getText().toString(); - if (text.length() > 0) { + if (!text.isEmpty()) { String profileNew = text.replaceAll("[^A-Za-z0-9_\\-]", "_"); if (!profileOld.equals(profileNew)) { renameConf(getApplicationContext(), profileOld, profileNew); @@ -132,7 +143,8 @@ private void editDialog() { } }) .setNegativeButton(android.R.string.cancel, - (dialog, whichButton) -> dialog.cancel()).show(); + (dialog, whichButton) -> dialog.cancel()) + .show(); } } @@ -142,12 +154,10 @@ private void deleteDialog() { new AlertDialog.Builder(this) .setTitle(R.string.confirm_profile_discard_title) .setMessage(R.string.confirm_profile_discard_message) - .setIcon(android.R.drawable.ic_dialog_alert) - .setCancelable(false) + .setIcon(R.drawable.ic_warning_24dp) .setPositiveButton(android.R.string.yes, (dialog, whichButton) -> { - String key = listItems.get(pos); - listItems.remove(pos); + String key = listItems.remove(pos); int last = listItems.size() - 1; if (last < 0) listItems.add(getString(R.string.profile)); if (last >= 0 && pos > last) @@ -156,7 +166,8 @@ private void deleteDialog() { removeConf(getApplicationContext(), key); }) .setNegativeButton(android.R.string.no, - (dialog, whichButton) -> dialog.cancel()).show(); + (dialog, whichButton) -> dialog.cancel()) + .show(); } } @@ -207,13 +218,17 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.menu_delete: deleteDialog(); break; + default: + return super.onOptionsItemSelected(item); } - return false; + + return true; } @Override public void onPause() { super.onPause(); + int pos = listView.getCheckedItemPosition(); if (pos >= 0 && pos < listItems.size()) { String profile = listItems.get(pos); diff --git a/app/src/main/java/ru/meefik/linuxdeploy/activity/PropertiesActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/activity/PropertiesActivity.java new file mode 100644 index 00000000..153e2245 --- /dev/null +++ b/app/src/main/java/ru/meefik/linuxdeploy/activity/PropertiesActivity.java @@ -0,0 +1,52 @@ +package ru.meefik.linuxdeploy.activity; + +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import ru.meefik.linuxdeploy.PrefStore; +import ru.meefik.linuxdeploy.R; +import ru.meefik.linuxdeploy.fragment.PropertiesFragment; + +public class PropertiesActivity extends AppCompatActivity { + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + PrefStore.setLocale(this); + setContentView(R.layout.activity_preference); + + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.frame_layout, new PropertiesFragment()) + .commit(); + + // Restore from conf file if open from main activity + if (getIntent().getBooleanExtra("restore", false)) { + PrefStore.restoreProperties(this); + } + } + + @Override + public void setTheme(int resId) { + super.setTheme(PrefStore.getTheme(this)); + } + + @Override + protected void onResume() { + super.onResume(); + + String titleMsg = getString(R.string.title_activity_properties) + + ": " + PrefStore.getProfileName(this); + setTitle(titleMsg); + } + + @Override + protected void onPause() { + super.onPause(); + + // Update configuration file + PrefStore.dumpProperties(this); + } +} diff --git a/app/src/main/java/ru/meefik/linuxdeploy/activity/RepositoryActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/activity/RepositoryActivity.java new file mode 100644 index 00000000..e210a050 --- /dev/null +++ b/app/src/main/java/ru/meefik/linuxdeploy/activity/RepositoryActivity.java @@ -0,0 +1,259 @@ +package ru.meefik.linuxdeploy.activity; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.EditText; +import android.widget.Toast; + +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedReader; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.GZIPInputStream; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import ru.meefik.linuxdeploy.PrefStore; +import ru.meefik.linuxdeploy.R; +import ru.meefik.linuxdeploy.adapter.RepositoryProfileAdapter; +import ru.meefik.linuxdeploy.model.RepositoryProfile; + +public class RepositoryActivity extends AppCompatActivity { + + private RepositoryProfileAdapter adapter; + + private boolean isDonated() { + return getPackageManager().checkSignatures(getPackageName(), "ru.meefik.donate") + == PackageManager.SIGNATURE_MATCH; + } + + private void importDialog(final RepositoryProfile repositoryProfile) { + final String name = repositoryProfile.getProfile(); + final String message = getString(R.string.repository_import_message, + repositoryProfile.getDescription(), + repositoryProfile.getSize()); + + AlertDialog.Builder dialog = new AlertDialog.Builder(this) + .setTitle(name) + .setMessage(message) + .setCancelable(false) + .setNegativeButton(android.R.string.no, (dialog13, which) -> dialog13.cancel()); + + if (isDonated()) { + dialog.setPositiveButton(R.string.repository_import_button, + (dialog1, whichButton) -> importProfile(name)); + } else { + dialog.setPositiveButton(R.string.repository_purchase_button, + (dialog12, whichButton) -> startActivity(new Intent(Intent.ACTION_VIEW, + Uri.parse("https://play.google.com/store/apps/details?id=ru.meefik.donate")))); + } + + dialog.show(); + } + + private void changeUrlDialog() { + View view = LayoutInflater.from(this).inflate(R.layout.edit_text_dialog, null); + EditText input = view.findViewById(R.id.edit_text); + input.setText(PrefStore.getRepositoryUrl(this)); + input.setSelection(input.getText().length()); + + new AlertDialog.Builder(this) + .setTitle(R.string.repository_change_url_title) + .setView(view) + .setPositiveButton(android.R.string.ok, + (dialog, whichButton) -> { + String text = input.getText().toString(); + if (text.isEmpty()) + text = getString(R.string.repository_url); + PrefStore.setRepositoryUrl(getApplicationContext(), text); + retrieveIndex(); + }) + .setNegativeButton(android.R.string.cancel, + (dialog, whichButton) -> dialog.cancel()) + .show(); + } + + private void retrieveIndex() { + String url = PrefStore.getRepositoryUrl(this); + + OkHttpClient client = new OkHttpClient.Builder() + .followRedirects(true) + .build(); + Request request = new Request.Builder() + .url(url + "/index.gz") + .build(); + + ProgressDialog dialog = new ProgressDialog(this); + dialog.setMessage(getString(R.string.loading_message)); + dialog.setCancelable(false); + dialog.show(); + + client.newCall(request).enqueue(new Callback() { + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + onFailure(); + } + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) { + if (response.isSuccessful()) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(response.body().byteStream())))) { + List repositoryProfiles = new ArrayList<>(); + String line; + RepositoryProfile repositoryProfile = null; + while ((line = reader.readLine()) != null) { + if (line.startsWith("PROFILE")) { + repositoryProfile = new RepositoryProfile(); + repositoryProfile.setProfile(line.split("=")[1]); + } else if (line.startsWith("DESC")) { + repositoryProfile.setDescription(line.split("=")[1]); + } else if (line.startsWith("TYPE")) { + repositoryProfile.setType(line.split("=")[1]); + } else if (line.startsWith("SIZE")) { + repositoryProfile.setSize(line.split("=")[1]); + repositoryProfiles.add(repositoryProfile); + } + } + + runOnUiThread(() -> { + adapter.setRepositoryProfiles(repositoryProfiles); + dialog.dismiss(); + }); + } catch (IOException e) { + onFailure(); + } + } else { + onFailure(); + } + } + + private void onFailure() { + runOnUiThread(() -> { + dialog.dismiss(); + Toast.makeText(RepositoryActivity.this, R.string.toast_loading_error, Toast.LENGTH_SHORT).show(); + }); + } + }); + } + + private void importProfile(String name) { + String url = PrefStore.getRepositoryUrl(this); + + OkHttpClient client = new OkHttpClient.Builder() + .followRedirects(true) + .build(); + Request request = new Request.Builder() + .url(url + "/index.gz") + .build(); + + ProgressDialog dialog = new ProgressDialog(this); + dialog.setMessage(getString(R.string.loading_message)); + dialog.setCancelable(false); + dialog.show(); + + client.newCall(request).enqueue(new Callback() { + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + onFailure(); + } + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) { + if (response.isSuccessful()) { + String conf = PrefStore.getEnvDir(RepositoryActivity.this) + "/config/" + name + ".conf"; + try (OutputStream os = new FileOutputStream(conf)) { + os.write(response.body().bytes()); + + runOnUiThread(dialog::dismiss); + PrefStore.changeProfile(RepositoryActivity.this, name); + finish(); + } catch (IOException e) { + onFailure(); + } + } else { + onFailure(); + } + } + + private void onFailure() { + runOnUiThread(() -> { + dialog.dismiss(); + Toast.makeText(RepositoryActivity.this, R.string.toast_loading_error, Toast.LENGTH_SHORT).show(); + }); + } + }); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + PrefStore.setLocale(this); + setContentView(R.layout.activity_repository); + + // RecyclerView Adapter + RecyclerView recyclerView = findViewById(R.id.repositoryView); + adapter = new RepositoryProfileAdapter(); + adapter.setOnItemClickListener(this::importDialog); + recyclerView.setAdapter(adapter); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); + + // Load list + retrieveIndex(); + } + + @Override + public void setTheme(int resId) { + super.setTheme(PrefStore.getTheme(this)); + } + + @Override + public void onResume() { + super.onResume(); + setTitle(R.string.title_activity_repository); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + PrefStore.setLocale(this); + getMenuInflater().inflate(R.menu.activity_repository, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_refresh: + retrieveIndex(); + break; + case R.id.menu_change_url: + changeUrlDialog(); + break; + default: + return super.onOptionsItemSelected(item); + } + + return true; + } +} diff --git a/app/src/main/java/ru/meefik/linuxdeploy/activity/SettingsActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/activity/SettingsActivity.java new file mode 100644 index 00000000..23dd2828 --- /dev/null +++ b/app/src/main/java/ru/meefik/linuxdeploy/activity/SettingsActivity.java @@ -0,0 +1,47 @@ +package ru.meefik.linuxdeploy.activity; + +import android.os.Bundle; + +import androidx.appcompat.app.AppCompatActivity; + +import ru.meefik.linuxdeploy.PrefStore; +import ru.meefik.linuxdeploy.R; +import ru.meefik.linuxdeploy.fragment.SettingsFragment; + +public class SettingsActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + PrefStore.setLocale(this); + setContentView(R.layout.activity_preference); + + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.frame_layout, new SettingsFragment()) + .commit(); + + // Restore from conf file + PrefStore.restoreSettings(this); + } + + @Override + public void setTheme(int resId) { + super.setTheme(PrefStore.getTheme(this)); + } + + @Override + public void onResume() { + super.onResume(); + + setTitle(R.string.title_activity_settings); + } + + @Override + public void onPause() { + super.onPause(); + + // update configuration file + PrefStore.dumpSettings(this); + } +} diff --git a/app/src/main/java/ru/meefik/linuxdeploy/adapter/MountAdapter.java b/app/src/main/java/ru/meefik/linuxdeploy/adapter/MountAdapter.java new file mode 100644 index 00000000..2a256477 --- /dev/null +++ b/app/src/main/java/ru/meefik/linuxdeploy/adapter/MountAdapter.java @@ -0,0 +1,120 @@ +package ru.meefik.linuxdeploy.adapter; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; + +import ru.meefik.linuxdeploy.R; +import ru.meefik.linuxdeploy.model.Mount; + +public class MountAdapter extends RecyclerView.Adapter { + + private List mounts; + private OnItemClickListener clickListener; + private OnItemDeleteListener deleteListener; + + public MountAdapter() { + this.mounts = new ArrayList<>(); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.mounts_row, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + holder.setMount(mounts.get(position)); + } + + @Override + public int getItemCount() { + return mounts == null ? 0 : mounts.size(); + } + + public void addMount(Mount mount) { + mounts.add(mount); + notifyDataSetChanged(); + } + + public void removeMount(Mount mount) { + mounts.remove(mount); + notifyDataSetChanged(); + } + + public void setMounts(List mounts) { + this.mounts.clear(); + for (String mount : mounts) { + String[] tmp = mount.split(":", 2); + this.mounts.add(new Mount(tmp[0], tmp[1])); + } + notifyDataSetChanged(); + } + + public List getMounts() { + List mounts = new ArrayList<>(); + for (Mount mount : this.mounts) { + mounts.add(mount.getSource() + ":" + mount.getTarget()); + } + return mounts; + } + + public void setOnItemClickListener(OnItemClickListener clickListener) { + this.clickListener = clickListener; + } + + public void setOnItemDeleteListener(OnItemDeleteListener deleteListener) { + this.deleteListener = deleteListener; + } + + public interface OnItemClickListener { + void onItemClick(Mount mount); + } + + public interface OnItemDeleteListener { + void onItemDelete(Mount mount); + } + + class ViewHolder extends RecyclerView.ViewHolder { + + private View view; + private TextView mountPoint; + private ImageView delete; + + ViewHolder(@NonNull View itemView) { + super(itemView); + + view = itemView; + mountPoint = itemView.findViewById(R.id.mount_point); + delete = itemView.findViewById(R.id.delete_mount); + } + + void setMount(Mount mount) { + if (mount.getTarget().isEmpty()) { + mountPoint.setText(mount.getSource()); + } else { + mountPoint.setText(mount.getSource() + " - " + mount.getTarget()); + } + + view.setOnClickListener(v -> { + if (clickListener != null) + clickListener.onItemClick(mount); + }); + + delete.setOnClickListener(v -> { + if (deleteListener != null) + deleteListener.onItemDelete(mount); + }); + } + } +} diff --git a/app/src/main/java/ru/meefik/linuxdeploy/adapter/RepositoryProfileAdapter.java b/app/src/main/java/ru/meefik/linuxdeploy/adapter/RepositoryProfileAdapter.java new file mode 100644 index 00000000..c16fbc63 --- /dev/null +++ b/app/src/main/java/ru/meefik/linuxdeploy/adapter/RepositoryProfileAdapter.java @@ -0,0 +1,112 @@ +package ru.meefik.linuxdeploy.adapter; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; + +import ru.meefik.linuxdeploy.R; +import ru.meefik.linuxdeploy.model.RepositoryProfile; + +public class RepositoryProfileAdapter extends RecyclerView.Adapter { + + private List repositoryProfiles; + private OnItemClickListener listener; + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.repository_row, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + holder.setRepository(repositoryProfiles.get(position)); + } + + @Override + public int getItemCount() { + return repositoryProfiles != null ? repositoryProfiles.size() : 0; + } + + public void setRepositoryProfiles(List repositoryProfiles) { + this.repositoryProfiles = repositoryProfiles; + notifyDataSetChanged(); + } + + public void setOnItemClickListener(OnItemClickListener listener) { + this.listener = listener; + } + + class ViewHolder extends RecyclerView.ViewHolder { + + private View view; + private TextView title; + private TextView subTitle; + private ImageView icon; + + ViewHolder(@NonNull View itemView) { + super(itemView); + + view = itemView; + title = itemView.findViewById(R.id.repo_entry_title); + subTitle = itemView.findViewById(R.id.repo_entry_subtitle); + icon = itemView.findViewById(R.id.repo_entry_icon); + } + + public void setRepository(RepositoryProfile repositoryProfile) { + int iconRes = R.raw.linux; + if (repositoryProfile.getType() != null) { + switch (repositoryProfile.getType()) { + case "alpine": + iconRes = R.raw.alpine; + break; + case "archlinux": + iconRes = R.raw.archlinux; + break; + case "centos": + iconRes = R.raw.centos; + break; + case "debian": + iconRes = R.raw.debian; + break; + case "fedora": + iconRes = R.raw.fedora; + break; + case "kali": + iconRes = R.raw.kali; + break; + case "slackware": + iconRes = R.raw.slackware; + break; + case "ubuntu": + iconRes = R.raw.ubuntu; + break; + } + } + + icon.setImageResource(iconRes); + title.setText(repositoryProfile.getProfile()); + if (repositoryProfile.getDescription() != null && !repositoryProfile.getDescription().isEmpty()) + subTitle.setText(repositoryProfile.getDescription()); + else + subTitle.setText(view.getContext().getString(R.string.repository_default_description)); + + view.setOnClickListener(v -> { + if (listener != null) + listener.onClick(repositoryProfile); + }); + } + } + + public interface OnItemClickListener { + void onClick(RepositoryProfile repositoryProfile); + } +} diff --git a/app/src/main/java/ru/meefik/linuxdeploy/PropertiesActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/fragment/PropertiesFragment.java similarity index 61% rename from app/src/main/java/ru/meefik/linuxdeploy/PropertiesActivity.java rename to app/src/main/java/ru/meefik/linuxdeploy/fragment/PropertiesFragment.java index 01781541..508b30d9 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/PropertiesActivity.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/fragment/PropertiesFragment.java @@ -1,149 +1,130 @@ -package ru.meefik.linuxdeploy; +package ru.meefik.linuxdeploy.fragment; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Bundle; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceGroup; -import android.preference.PreferenceScreen; -import android.view.MenuItem; -public class PropertiesActivity extends AppCompatPreferenceActivity implements +import androidx.preference.EditTextPreference; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; + +import ru.meefik.linuxdeploy.PrefStore; +import ru.meefik.linuxdeploy.R; +import ru.meefik.linuxdeploy.activity.MountsActivity; +import ru.meefik.linuxdeploy.activity.PropertiesActivity; + +public class PropertiesFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener, OnSharedPreferenceChangeListener { @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - PrefStore.setLocale(this); - + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { getPreferenceManager().setSharedPreferencesName(PrefStore.getPropertiesSharedName()); - // Restore from conf file if open from main activity - if (getIntent().getBooleanExtra("restore", false)) { - PrefStore.restoreProperties(this); - } - - Bundle b = getIntent().getExtras(); - int pref = 0; - if (b != null) - pref = b.getInt("pref"); - switch (pref) { - case 1: - addPreferencesFromResource(R.xml.properties_ssh); - break; - case 2: - addPreferencesFromResource(R.xml.properties_vnc); - break; - case 3: - addPreferencesFromResource(R.xml.properties_x11); - break; - case 4: - addPreferencesFromResource(R.xml.properties_fb); - break; - case 5: - addPreferencesFromResource(R.xml.properties_run_parts); - break; - case 6: - addPreferencesFromResource(R.xml.properties_sysv); - break; - case 7: - addPreferencesFromResource(R.xml.properties_pulse); - break; - default: - addPreferencesFromResource(R.xml.properties); + Intent i = getActivity().getIntent(); + if (i != null) { + switch (i.getIntExtra("pref", 0)) { + case 1: + setPreferencesFromResource(R.xml.properties_ssh, rootKey); + break; + case 2: + setPreferencesFromResource(R.xml.properties_vnc, rootKey); + break; + case 3: + setPreferencesFromResource(R.xml.properties_x11, rootKey); + break; + case 4: + setPreferencesFromResource(R.xml.properties_fb, rootKey); + break; + case 5: + setPreferencesFromResource(R.xml.properties_run_parts, rootKey); + break; + case 6: + setPreferencesFromResource(R.xml.properties_sysv, rootKey); + break; + case 7: + setPreferencesFromResource(R.xml.properties_pulse, rootKey); + break; + default: + setPreferencesFromResource(R.xml.properties, rootKey); + } } initSummaries(getPreferenceScreen()); } - @Override - public void setTheme(int resId) { - super.setTheme(PrefStore.getTheme(this)); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - finish(); - break; - } - return false; - } - @Override public void onResume() { super.onResume(); - - String titleMsg = getString(R.string.title_activity_properties) - + ": " + PrefStore.getProfileName(this); - setTitle(titleMsg); - getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); } @Override public void onPause() { super.onPause(); - getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); - - // update configuration file - PrefStore.dumpProperties(this); } @Override public boolean onPreferenceClick(Preference preference) { - if (preference.getKey().equals("ssh_properties")) { - Intent intent = new Intent(this, PropertiesActivity.class); - Bundle b = new Bundle(); - b.putInt("pref", 1); - intent.putExtras(b); - startActivity(intent); - } - if (preference.getKey().equals("gui_properties")) { - ListPreference graphics = (ListPreference) findPreference("graphics"); - Intent intent = new Intent(this, PropertiesActivity.class); - Bundle b = new Bundle(); - if (graphics.getValue().equals("vnc")) { - b.putInt("pref", 2); + switch (preference.getKey()) { + case "ssh_properties": { + Intent intent = new Intent(getContext(), PropertiesActivity.class); + intent.putExtra("pref", 1); + startActivity(intent); + break; } - if (graphics.getValue().equals("x11")) { - b.putInt("pref", 3); + case "gui_properties": { + Intent intent = new Intent(getContext(), PropertiesActivity.class); + + ListPreference graphics = findPreference("graphics"); + switch (graphics.getValue()) { + case "vnc": + intent.putExtra("pref", 2); + break; + case "x11": + intent.putExtra("pref", 3); + break; + case "fb": + intent.putExtra("pref", 4); + break; + } + + startActivity(intent); + break; } - if (graphics.getValue().equals("fb")) { - b.putInt("pref", 4); + case "init_properties": { + Intent intent = new Intent(getContext(), PropertiesActivity.class); + + ListPreference init = findPreference("init"); + switch (init.getValue()) { + case "run-parts": + intent.putExtra("pref", 5); + break; + case "sysv": + intent.putExtra("pref", 6); + break; + } + + startActivity(intent); + break; } - intent.putExtras(b); - startActivity(intent); - } - if (preference.getKey().equals("init_properties")) { - ListPreference init = (ListPreference) findPreference("init"); - Intent intent = new Intent(this, PropertiesActivity.class); - Bundle b = new Bundle(); - if (init.getValue().equals("run-parts")) { - b.putInt("pref", 5); + case "pulse_properties": { + Intent intent = new Intent(getContext(), PropertiesActivity.class); + intent.putExtra("pref", 7); + startActivity(intent); + break; } - if (init.getValue().equals("sysv")) { - b.putInt("pref", 6); + case "mounts_editor": { + Intent intent = new Intent(getContext(), MountsActivity.class); + startActivity(intent); + break; } - intent.putExtras(b); - startActivity(intent); - } - if (preference.getKey().equals("pulse_properties")) { - Intent intent = new Intent(this, PropertiesActivity.class); - Bundle b = new Bundle(); - b.putInt("pref", 7); - intent.putExtras(b); - startActivity(intent); - } - if (preference.getKey().equals("mounts_editor")) { - Intent intent = new Intent(this, MountsActivity.class); - startActivity(intent); } + return true; } @@ -190,21 +171,21 @@ private void setSummary(Preference pref, boolean init) { pref.setSummary(listPref.getEntry()); if (listPref.getKey().equals("distrib")) { - ListPreference suite = (ListPreference) findPreference("suite"); - ListPreference architecture = (ListPreference) findPreference("arch"); - EditTextPreference sourcepath = (EditTextPreference) findPreference("source_path"); + ListPreference suite = findPreference("suite"); + ListPreference architecture = findPreference("arch"); + EditTextPreference sourcepath = findPreference("source_path"); String distributionStr = listPref.getValue(); // suite - int suiteValuesId = PrefStore.getResourceId(this, + int suiteValuesId = PrefStore.getResourceId(getContext(), distributionStr + "_suite_values", "array"); if (suiteValuesId > 0) { suite.setEntries(suiteValuesId); suite.setEntryValues(suiteValuesId); } if (init) { - int suiteId = PrefStore.getResourceId(this, distributionStr + int suiteId = PrefStore.getResourceId(getContext(), distributionStr + "_suite", "string"); if (suiteId > 0) { String suiteStr = getString(suiteId); @@ -216,14 +197,14 @@ private void setSummary(Preference pref, boolean init) { suite.setEnabled(true); // architecture - int architectureValuesId = PrefStore.getResourceId(this, + int architectureValuesId = PrefStore.getResourceId(getContext(), distributionStr + "_arch_values", "array"); if (suiteValuesId > 0) { architecture.setEntries(architectureValuesId); architecture.setEntryValues(architectureValuesId); } if (init || architecture.getValue().length() == 0) { - int architectureId = PrefStore.getResourceId(this, + int architectureId = PrefStore.getResourceId(getContext(), PrefStore.getArch() + "_" + distributionStr + "_arch", "string"); if (architectureId > 0) { @@ -238,7 +219,7 @@ private void setSummary(Preference pref, boolean init) { // source path if (init || sourcepath.getText().length() == 0) { int sourcepathId = PrefStore - .getResourceId(this, PrefStore.getArch() + "_" + .getResourceId(getContext(), PrefStore.getArch() + "_" + distributionStr + "_source_path", "string"); if (sourcepathId > 0) { sourcepath.setText(getString(sourcepathId)); @@ -263,13 +244,13 @@ private void setSummary(Preference pref, boolean init) { } } if (listPref.getKey().equals("arch") && init) { - ListPreference distribution = (ListPreference) findPreference("distrib"); - EditTextPreference sourcepath = (EditTextPreference) findPreference("source_path"); + ListPreference distribution = findPreference("distrib"); + EditTextPreference sourcepath = findPreference("source_path"); String architectureStr = PrefStore.getArch(listPref.getValue()); String distributionStr = distribution.getValue(); - int sourcePathId = PrefStore.getResourceId(this, architectureStr + int sourcePathId = PrefStore.getResourceId(getContext(), architectureStr + "_" + distributionStr + "_source_path", "string"); if (sourcePathId > 0) { sourcepath.setText(getString(sourcePathId)); @@ -278,9 +259,9 @@ private void setSummary(Preference pref, boolean init) { sourcepath.setSummary(sourcepath.getText()); } if (listPref.getKey().equals("target_type")) { - EditTextPreference targetpath = (EditTextPreference) findPreference("target_path"); - EditTextPreference disksize = (EditTextPreference) findPreference("disk_size"); - ListPreference fstype = (ListPreference) findPreference("fs_type"); + EditTextPreference targetpath = findPreference("target_path"); + EditTextPreference disksize = findPreference("disk_size"); + ListPreference fstype = findPreference("fs_type"); switch (listPref.getValue()) { case "file": diff --git a/app/src/main/java/ru/meefik/linuxdeploy/SettingsActivity.java b/app/src/main/java/ru/meefik/linuxdeploy/fragment/SettingsFragment.java similarity index 63% rename from app/src/main/java/ru/meefik/linuxdeploy/SettingsActivity.java rename to app/src/main/java/ru/meefik/linuxdeploy/fragment/SettingsFragment.java index a375bab4..a0d58ca4 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/SettingsActivity.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/fragment/SettingsFragment.java @@ -1,52 +1,45 @@ -package ru.meefik.linuxdeploy; +package ru.meefik.linuxdeploy.fragment; import android.Manifest; import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.pm.PackageManager; import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceGroup; -import android.preference.PreferenceScreen; import androidx.appcompat.app.AlertDialog; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; - -public class SettingsActivity extends AppCompatPreferenceActivity implements +import androidx.preference.CheckBoxPreference; +import androidx.preference.EditTextPreference; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; + +import ru.meefik.linuxdeploy.EnvUtils; +import ru.meefik.linuxdeploy.PrefStore; +import ru.meefik.linuxdeploy.R; +import ru.meefik.linuxdeploy.RemoveEnvTask; +import ru.meefik.linuxdeploy.UpdateEnvTask; +import ru.meefik.linuxdeploy.receiver.BootReceiver; + +public class SettingsFragment extends PreferenceFragmentCompat implements OnSharedPreferenceChangeListener, Preference.OnPreferenceClickListener { @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - PrefStore.setLocale(this); - + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { getPreferenceManager().setSharedPreferencesName(PrefStore.getSettingsSharedName()); - - // Restore from conf file - PrefStore.restoreSettings(this); - - addPreferencesFromResource(R.xml.settings); - + setPreferencesFromResource(R.xml.settings, rootKey); initSummaries(getPreferenceScreen()); } - @Override - public void setTheme(int resId) { - super.setTheme(PrefStore.getTheme(this)); - } - @Override public void onResume() { super.onResume(); - setTitle(R.string.title_activity_settings); getPreferenceScreen().getSharedPreferences() .registerOnSharedPreferenceChangeListener(this); } @@ -57,20 +50,20 @@ public void onPause() { getPreferenceScreen().getSharedPreferences() .unregisterOnSharedPreferenceChangeListener(this); - - // update configuration file - PrefStore.dumpSettings(this); } @Override public boolean onPreferenceClick(Preference preference) { - if (preference.getKey().equals("installenv")) { - updateEnvDialog(); - } - if (preference.getKey().equals("removeenv")) { - removeEnvDialog(); + switch (preference.getKey()) { + case "installenv": + updateEnvDialog(); + return true; + case "removeenv": + removeEnvDialog(); + return true; + default: + return false; } - return true; } @Override @@ -80,43 +73,44 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin switch (key) { case "is_telnet": // start/stop telnetd - EnvUtils.execService(getBaseContext(), "telnetd", null); + EnvUtils.execService(getContext(), "telnetd", null); break; case "telnet_port": // restart telnetd - EnvUtils.execService(getBaseContext(), "telnetd", "restart"); + EnvUtils.execService(getContext(), "telnetd", "restart"); // restart httpd - EnvUtils.execService(getBaseContext(), "httpd", "restart"); + EnvUtils.execService(getContext(), "httpd", "restart"); + break; case "telnet_localhost": // restart telnetd - EnvUtils.execService(getBaseContext(), "telnetd", "restart"); + EnvUtils.execService(getContext(), "telnetd", "restart"); break; case "is_http": // start/stop httpd - EnvUtils.execService(getBaseContext(), "httpd", null); + EnvUtils.execService(getContext(), "httpd", null); break; case "http_port": case "http_conf": // restart httpd - EnvUtils.execService(getBaseContext(), "httpd", "restart"); + EnvUtils.execService(getContext(), "httpd", "restart"); break; case "autostart": // set autostart settings - int autostartFlag = (PrefStore.isAutostart(this) ? + int autostartFlag = (PrefStore.isAutostart(getContext()) ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED); - ComponentName bootComponent = new ComponentName(this, BootReceiver.class); - getPackageManager().setComponentEnabledSetting(bootComponent, autostartFlag, + ComponentName bootComponent = new ComponentName(getContext(), BootReceiver.class); + getContext().getPackageManager().setComponentEnabledSetting(bootComponent, autostartFlag, PackageManager.DONT_KILL_APP); break; case "stealth": // set stealth mode // Run app without launcher: am start -n ru.meefik.linuxdeploy/.MainActivity - int stealthFlag = PrefStore.isStealth(this) ? + int stealthFlag = PrefStore.isStealth(getContext()) ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED : PackageManager.COMPONENT_ENABLED_STATE_ENABLED; - ComponentName mainComponent = new ComponentName(getPackageName(), getPackageName() + ".Launcher"); - getPackageManager().setComponentEnabledSetting(mainComponent, stealthFlag, + ComponentName mainComponent = new ComponentName(getContext().getPackageName(), getContext().getPackageName() + ".Launcher"); + getContext().getPackageManager().setComponentEnabledSetting(mainComponent, stealthFlag, PackageManager.DONT_KILL_APP); break; } @@ -139,20 +133,25 @@ private void setSummary(Preference pref, boolean init) { EditTextPreference editPref = (EditTextPreference) pref; pref.setSummary(editPref.getText()); - if (editPref.getKey().equals("env_dir") && !init) { - editPref.setText(PrefStore.getEnvDir(this)); - pref.setSummary(editPref.getText()); - } - - if (editPref.getKey().equals("http_conf") && - editPref.getText().isEmpty()) { - editPref.setText(PrefStore.getHttpConf(this)); - pref.setSummary(editPref.getText()); - } - - if (editPref.getKey().equals("logfile") && !init) { - editPref.setText(PrefStore.getLogFile(this)); - pref.setSummary(editPref.getText()); + switch (editPref.getKey()) { + case "env_dir": + if (!init) { + editPref.setText(PrefStore.getEnvDir(getContext())); + pref.setSummary(editPref.getText()); + } + break; + case "http_conf": + if (editPref.getText().isEmpty()) { + editPref.setText(PrefStore.getHttpConf(getContext())); + pref.setSummary(editPref.getText()); + } + break; + case "logfile": + if (!init) { + editPref.setText(PrefStore.getLogFile(getContext())); + pref.setSummary(editPref.getText()); + } + break; } } @@ -171,8 +170,8 @@ private void setSummary(Preference pref, boolean init) { } private void updateEnvDialog() { - final Context context = this; - new AlertDialog.Builder(this) + final Context context = getContext(); + new AlertDialog.Builder(getContext()) .setTitle(R.string.title_installenv_preference) .setMessage(R.string.message_installenv_confirm_dialog) .setIcon(android.R.drawable.ic_dialog_alert) @@ -184,8 +183,8 @@ private void updateEnvDialog() { } private void removeEnvDialog() { - final Context context = this; - new AlertDialog.Builder(this) + final Context context = getContext(); + new AlertDialog.Builder(getContext()) .setTitle(R.string.title_removeenv_preference) .setMessage(R.string.message_removeenv_confirm_dialog) .setIcon(android.R.drawable.ic_dialog_alert) @@ -201,10 +200,10 @@ private void removeEnvDialog() { */ private void requestWritePermissions() { int REQUEST_WRITE_STORAGE = 112; - boolean hasPermission = (ContextCompat.checkSelfPermission(this, + boolean hasPermission = (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED); if (!hasPermission) { - ActivityCompat.requestPermissions(this, + ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE); } } diff --git a/app/src/main/java/ru/meefik/linuxdeploy/model/Mount.java b/app/src/main/java/ru/meefik/linuxdeploy/model/Mount.java new file mode 100644 index 00000000..763f2639 --- /dev/null +++ b/app/src/main/java/ru/meefik/linuxdeploy/model/Mount.java @@ -0,0 +1,30 @@ +package ru.meefik.linuxdeploy.model; + +public class Mount { + private String source; + private String target; + + public Mount() { + } + + public Mount(String source, String target) { + this.source = source; + this.target = target; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } +} diff --git a/app/src/main/java/ru/meefik/linuxdeploy/model/RepositoryProfile.java b/app/src/main/java/ru/meefik/linuxdeploy/model/RepositoryProfile.java new file mode 100644 index 00000000..9bb2f708 --- /dev/null +++ b/app/src/main/java/ru/meefik/linuxdeploy/model/RepositoryProfile.java @@ -0,0 +1,44 @@ +package ru.meefik.linuxdeploy.model; + +public class RepositoryProfile { + private String profile; + private String description; + private String type; + private String size; + + public RepositoryProfile() { + // Empty constructor + } + + public String getProfile() { + return profile; + } + + public void setProfile(String profile) { + this.profile = profile; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSize() { + return size; + } + + public void setSize(String size) { + this.size = size; + } +} diff --git a/app/src/main/java/ru/meefik/linuxdeploy/ActionReceiver.java b/app/src/main/java/ru/meefik/linuxdeploy/receiver/ActionReceiver.java similarity index 95% rename from app/src/main/java/ru/meefik/linuxdeploy/ActionReceiver.java rename to app/src/main/java/ru/meefik/linuxdeploy/receiver/ActionReceiver.java index 0f4831ef..5abf5cb0 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/ActionReceiver.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/receiver/ActionReceiver.java @@ -1,4 +1,4 @@ -package ru.meefik.linuxdeploy; +package ru.meefik.linuxdeploy.receiver; import android.app.NotificationManager; import android.content.BroadcastReceiver; @@ -7,6 +7,10 @@ import androidx.core.app.NotificationCompat; +import ru.meefik.linuxdeploy.EnvUtils; +import ru.meefik.linuxdeploy.R; +import ru.meefik.linuxdeploy.activity.MainActivity; + import static ru.meefik.linuxdeploy.App.SERVICE_CHANNEL_ID; public class ActionReceiver extends BroadcastReceiver { diff --git a/app/src/main/java/ru/meefik/linuxdeploy/BootReceiver.java b/app/src/main/java/ru/meefik/linuxdeploy/receiver/BootReceiver.java similarity index 91% rename from app/src/main/java/ru/meefik/linuxdeploy/BootReceiver.java rename to app/src/main/java/ru/meefik/linuxdeploy/receiver/BootReceiver.java index 705beedd..0d22cf5e 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/BootReceiver.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/receiver/BootReceiver.java @@ -1,9 +1,12 @@ -package ru.meefik.linuxdeploy; +package ru.meefik.linuxdeploy.receiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import ru.meefik.linuxdeploy.EnvUtils; +import ru.meefik.linuxdeploy.PrefStore; + public class BootReceiver extends BroadcastReceiver { @Override diff --git a/app/src/main/java/ru/meefik/linuxdeploy/receiver/NetworkReceiver.java b/app/src/main/java/ru/meefik/linuxdeploy/receiver/NetworkReceiver.java new file mode 100644 index 00000000..fac59464 --- /dev/null +++ b/app/src/main/java/ru/meefik/linuxdeploy/receiver/NetworkReceiver.java @@ -0,0 +1,43 @@ +package ru.meefik.linuxdeploy.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; + +import ru.meefik.linuxdeploy.EnvUtils; + +public class NetworkReceiver extends BroadcastReceiver { + + @Override + public void onReceive(final Context context, Intent intent) { + if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { + ConnectivityManager cm = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + + boolean isConnected; + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + Network activeNetwork = cm.getActiveNetwork(); + NetworkCapabilities networkCapabilities = cm.getNetworkCapabilities(activeNetwork); + + isConnected = networkCapabilities != null + && (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + || networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) + || networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)); + } else { + NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo(); + isConnected = activeNetworkInfo != null && activeNetworkInfo.isConnected(); + } + + if (isConnected) { + EnvUtils.execService(context, "start", "core/net"); + } else { + EnvUtils.execService(context, "stop", "core/net"); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/meefik/linuxdeploy/PowerReceiver.java b/app/src/main/java/ru/meefik/linuxdeploy/receiver/PowerReceiver.java similarity index 86% rename from app/src/main/java/ru/meefik/linuxdeploy/PowerReceiver.java rename to app/src/main/java/ru/meefik/linuxdeploy/receiver/PowerReceiver.java index e5a55cf2..c0d52aa6 100644 --- a/app/src/main/java/ru/meefik/linuxdeploy/PowerReceiver.java +++ b/app/src/main/java/ru/meefik/linuxdeploy/receiver/PowerReceiver.java @@ -1,9 +1,11 @@ -package ru.meefik.linuxdeploy; +package ru.meefik.linuxdeploy.receiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import ru.meefik.linuxdeploy.EnvUtils; + public class PowerReceiver extends BroadcastReceiver { @Override diff --git a/app/src/main/res/drawable-hdpi/ic_action_more_dark.png b/app/src/main/res/drawable-hdpi/ic_action_more_dark.png deleted file mode 100644 index ddb4dcc9..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_more_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_more_light.png b/app/src/main/res/drawable-hdpi/ic_action_more_light.png deleted file mode 100644 index 44ab0560..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_more_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_properties_dark.png b/app/src/main/res/drawable-hdpi/ic_action_properties_dark.png deleted file mode 100644 index 37d89e87..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_properties_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_properties_light.png b/app/src/main/res/drawable-hdpi/ic_action_properties_light.png deleted file mode 100644 index d944dac5..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_properties_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_start_dark.png b/app/src/main/res/drawable-hdpi/ic_action_start_dark.png deleted file mode 100644 index cd00acb2..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_start_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_start_light.png b/app/src/main/res/drawable-hdpi/ic_action_start_light.png deleted file mode 100644 index 12d93deb..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_start_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_stop_dark.png b/app/src/main/res/drawable-hdpi/ic_action_stop_dark.png deleted file mode 100644 index ff57d76c..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_stop_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_stop_light.png b/app/src/main/res/drawable-hdpi/ic_action_stop_light.png deleted file mode 100644 index 50d4365e..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_stop_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-ldpi/ic_action_properties_dark.png b/app/src/main/res/drawable-ldpi/ic_action_properties_dark.png deleted file mode 100644 index 11a71e2e..00000000 Binary files a/app/src/main/res/drawable-ldpi/ic_action_properties_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-ldpi/ic_action_properties_light.png b/app/src/main/res/drawable-ldpi/ic_action_properties_light.png deleted file mode 100644 index e0ff07a3..00000000 Binary files a/app/src/main/res/drawable-ldpi/ic_action_properties_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-ldpi/ic_action_start_dark.png b/app/src/main/res/drawable-ldpi/ic_action_start_dark.png deleted file mode 100644 index 3686f069..00000000 Binary files a/app/src/main/res/drawable-ldpi/ic_action_start_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-ldpi/ic_action_start_light.png b/app/src/main/res/drawable-ldpi/ic_action_start_light.png deleted file mode 100644 index e487f58c..00000000 Binary files a/app/src/main/res/drawable-ldpi/ic_action_start_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-ldpi/ic_action_stop_dark.png b/app/src/main/res/drawable-ldpi/ic_action_stop_dark.png deleted file mode 100644 index c183a8a1..00000000 Binary files a/app/src/main/res/drawable-ldpi/ic_action_stop_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-ldpi/ic_action_stop_light.png b/app/src/main/res/drawable-ldpi/ic_action_stop_light.png deleted file mode 100644 index 76a5d264..00000000 Binary files a/app/src/main/res/drawable-ldpi/ic_action_stop_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_more_dark.png b/app/src/main/res/drawable-mdpi/ic_action_more_dark.png deleted file mode 100644 index b07955b5..00000000 Binary files a/app/src/main/res/drawable-mdpi/ic_action_more_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_more_light.png b/app/src/main/res/drawable-mdpi/ic_action_more_light.png deleted file mode 100644 index cef6b9b4..00000000 Binary files a/app/src/main/res/drawable-mdpi/ic_action_more_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_properties_dark.png b/app/src/main/res/drawable-mdpi/ic_action_properties_dark.png deleted file mode 100644 index d6c82c4c..00000000 Binary files a/app/src/main/res/drawable-mdpi/ic_action_properties_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_properties_light.png b/app/src/main/res/drawable-mdpi/ic_action_properties_light.png deleted file mode 100644 index a53c5a55..00000000 Binary files a/app/src/main/res/drawable-mdpi/ic_action_properties_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_start_dark.png b/app/src/main/res/drawable-mdpi/ic_action_start_dark.png deleted file mode 100644 index 3881198d..00000000 Binary files a/app/src/main/res/drawable-mdpi/ic_action_start_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_start_light.png b/app/src/main/res/drawable-mdpi/ic_action_start_light.png deleted file mode 100644 index 48f451ba..00000000 Binary files a/app/src/main/res/drawable-mdpi/ic_action_start_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_stop_dark.png b/app/src/main/res/drawable-mdpi/ic_action_stop_dark.png deleted file mode 100644 index 27202add..00000000 Binary files a/app/src/main/res/drawable-mdpi/ic_action_stop_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_stop_light.png b/app/src/main/res/drawable-mdpi/ic_action_stop_light.png deleted file mode 100644 index 23cd4417..00000000 Binary files a/app/src/main/res/drawable-mdpi/ic_action_stop_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_more_dark.png b/app/src/main/res/drawable-xhdpi/ic_action_more_dark.png deleted file mode 100644 index 276d9b8f..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_more_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_more_light.png b/app/src/main/res/drawable-xhdpi/ic_action_more_light.png deleted file mode 100644 index 44b7c614..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_more_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_properties_dark.png b/app/src/main/res/drawable-xhdpi/ic_action_properties_dark.png deleted file mode 100644 index b7cf0578..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_properties_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_properties_light.png b/app/src/main/res/drawable-xhdpi/ic_action_properties_light.png deleted file mode 100644 index 5408366e..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_properties_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_start_dark.png b/app/src/main/res/drawable-xhdpi/ic_action_start_dark.png deleted file mode 100644 index 8fa2f63f..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_start_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_start_light.png b/app/src/main/res/drawable-xhdpi/ic_action_start_light.png deleted file mode 100644 index 82f5b561..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_start_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_stop_dark.png b/app/src/main/res/drawable-xhdpi/ic_action_stop_dark.png deleted file mode 100644 index 3b4ebab6..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_stop_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_stop_light.png b/app/src/main/res/drawable-xhdpi/ic_action_stop_light.png deleted file mode 100644 index f26ac2c8..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_stop_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_more_dark.png b/app/src/main/res/drawable-xxhdpi/ic_action_more_dark.png deleted file mode 100644 index ec25f349..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_more_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_more_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_more_light.png deleted file mode 100644 index fce82fd3..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_more_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_properties_dark.png b/app/src/main/res/drawable-xxhdpi/ic_action_properties_dark.png deleted file mode 100644 index d1f23c8d..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_properties_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_properties_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_properties_light.png deleted file mode 100644 index c872374a..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_properties_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_start_dark.png b/app/src/main/res/drawable-xxhdpi/ic_action_start_dark.png deleted file mode 100644 index 8ed909d1..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_start_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_start_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_start_light.png deleted file mode 100644 index 4aa78ac4..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_start_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_stop_dark.png b/app/src/main/res/drawable-xxhdpi/ic_action_stop_dark.png deleted file mode 100644 index 50466e4a..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_stop_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_stop_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_stop_light.png deleted file mode 100644 index f8743404..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_stop_light.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_add_24dp.xml b/app/src/main/res/drawable/ic_add_24dp.xml new file mode 100644 index 00000000..89fefeaf --- /dev/null +++ b/app/src/main/res/drawable/ic_add_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_close_black_24dp.xml b/app/src/main/res/drawable/ic_close_24dp.xml similarity index 91% rename from app/src/main/res/drawable/ic_close_black_24dp.xml rename to app/src/main/res/drawable/ic_close_24dp.xml index ede4b710..c94c5a0f 100644 --- a/app/src/main/res/drawable/ic_close_black_24dp.xml +++ b/app/src/main/res/drawable/ic_close_24dp.xml @@ -1,4 +1,5 @@ + + diff --git a/app/src/main/res/drawable/ic_edit_24dp.xml b/app/src/main/res/drawable/ic_edit_24dp.xml new file mode 100644 index 00000000..763102a5 --- /dev/null +++ b/app/src/main/res/drawable/ic_edit_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_exit_to_app_black_24dp.xml b/app/src/main/res/drawable/ic_exit_to_app_24dp.xml similarity index 92% rename from app/src/main/res/drawable/ic_exit_to_app_black_24dp.xml rename to app/src/main/res/drawable/ic_exit_to_app_24dp.xml index 6f40d772..48e4f76e 100644 --- a/app/src/main/res/drawable/ic_exit_to_app_black_24dp.xml +++ b/app/src/main/res/drawable/ic_exit_to_app_24dp.xml @@ -1,4 +1,5 @@ + + diff --git a/app/src/main/res/drawable/ic_refresh_24dp.xml b/app/src/main/res/drawable/ic_refresh_24dp.xml new file mode 100644 index 00000000..50a3a041 --- /dev/null +++ b/app/src/main/res/drawable/ic_refresh_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_search_black_24dp.xml b/app/src/main/res/drawable/ic_search_24dp.xml similarity index 93% rename from app/src/main/res/drawable/ic_search_black_24dp.xml rename to app/src/main/res/drawable/ic_search_24dp.xml index affc7ba2..ce4e71c6 100644 --- a/app/src/main/res/drawable/ic_search_black_24dp.xml +++ b/app/src/main/res/drawable/ic_search_24dp.xml @@ -1,4 +1,5 @@ + + diff --git a/app/src/main/res/drawable/ic_tune_24dp.xml b/app/src/main/res/drawable/ic_tune_24dp.xml new file mode 100644 index 00000000..0b8928c7 --- /dev/null +++ b/app/src/main/res/drawable/ic_tune_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_warning_24dp.xml b/app/src/main/res/drawable/ic_warning_24dp.xml new file mode 100644 index 00000000..7282be50 --- /dev/null +++ b/app/src/main/res/drawable/ic_warning_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout-land/content_main.xml b/app/src/main/res/layout-land/content_main.xml index 70fdc924..3fcfbde5 100644 --- a/app/src/main/res/layout-land/content_main.xml +++ b/app/src/main/res/layout-land/content_main.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" - tools:context=".MainActivity" + tools:context=".activity.MainActivity" tools:showIn="@layout/activity_main"> + tools:context=".activity.AboutActivity"> + tools:context=".activity.FullscreenActivity"> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 151a2d99..bc0e3cc8 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,7 +4,6 @@ android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" - android:fitsSystemWindows="true" tools:openDrawer="start"> @@ -14,6 +13,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" + android:fitsSystemWindows="false" app:menu="@menu/activity_main_drawer" /> diff --git a/app/src/main/res/layout/activity_mounts.xml b/app/src/main/res/layout/activity_mounts.xml index fa5abc3a..c4c4d43c 100644 --- a/app/src/main/res/layout/activity_mounts.xml +++ b/app/src/main/res/layout/activity_mounts.xml @@ -1,17 +1,6 @@ - - - - - + tools:context=".activity.MountsActivity" /> diff --git a/app/src/main/res/layout/activity_preference.xml b/app/src/main/res/layout/activity_preference.xml new file mode 100644 index 00000000..76a157b1 --- /dev/null +++ b/app/src/main/res/layout/activity_preference.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_profiles.xml b/app/src/main/res/layout/activity_profiles.xml index ac9b13c9..58bd6a6e 100644 --- a/app/src/main/res/layout/activity_profiles.xml +++ b/app/src/main/res/layout/activity_profiles.xml @@ -2,7 +2,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".ProfilesActivity"> + tools:context=".activity.ProfilesActivity"> - - - - + android:scrollbars="vertical" + tools:context=".activity.RepositoryActivity" /> diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index ed8cf048..53e41a61 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" - tools:context=".MainActivity" + tools:context=".activity.MainActivity" tools:showIn="@layout/activity_main"> + android:src="@drawable/ic_tune_24dp" /> diff --git a/app/src/main/res/layout/edit_text_dialog.xml b/app/src/main/res/layout/edit_text_dialog.xml new file mode 100644 index 00000000..fe41f0e5 --- /dev/null +++ b/app/src/main/res/layout/edit_text_dialog.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/mounts_row.xml b/app/src/main/res/layout/mounts_row.xml index 07d47682..7a28cde9 100644 --- a/app/src/main/res/layout/mounts_row.xml +++ b/app/src/main/res/layout/mounts_row.xml @@ -1,10 +1,14 @@ @@ -17,15 +21,15 @@ android:paddingRight="4dp" android:textSize="18sp" /> -