1
0
Fork 0
mirror of https://github.com/syncthing/syncthing-android.git synced 2024-11-26 06:11:19 +00:00

Added graphical repo folder picker (closes #40).

This commit is contained in:
Felix Ableitner 2014-06-27 17:24:23 +02:00
parent 35644c355b
commit ca0020b176
18 changed files with 322 additions and 36 deletions

View file

@ -95,7 +95,7 @@ def getVersionCodeFromManifest() {
return Integer.parseInt(matcher.group(1))
}
task buildNative(type:Exec) {
task buildNative(type: Exec) {
outputs.upToDateWhen { false }
executable = './build-native.sh'
}

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.nutomic.syncthingandroid"
@ -6,27 +6,28 @@
android:versionName="0.3.10"
tools:ignore="GradleOverrides" >
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" />
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="false"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar" >
<activity
android:name=".gui.MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".gui.WebGuiActivity"
android:label="@string/web_gui_title" >
@ -34,7 +35,6 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".gui.MainActivity" />
</activity>
<activity
android:name=".gui.SettingsActivity"
android:label="@string/settings_title" >
@ -47,19 +47,28 @@
<activity
android:name=".gui.RepoSettingsActivity"
android:theme="@style/DialogWhenLargeCompat" >
android:theme="@style/DialogWhenLarge" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".gui.MainActivity" />
</activity>
<activity
android:name=".gui.NodeSettingsActivity"
android:theme="@style/DialogWhenLargeCompat" >
android:theme="@style/DialogWhenLarge" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".gui.MainActivity" />
</activity>
<activity
android:name=".gui.FolderPickerActivity"
android:label="@string/title_activity_folder_picker" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".gui.RepoSettingsActivity" />
<meta-data
android:name="android.support.UI_OPTIONS"
android:value="splitActionBarWhenNarrow" />
</activity>
</application>
</manifest>

View file

@ -0,0 +1,207 @@
package com.nutomic.syncthingandroid.gui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Stack;
/**
* Activity that allows selecting a directory in the local file system.
*/
public class FolderPickerActivity extends ActionBarActivity
implements AdapterView.OnItemClickListener, SyncthingService.OnApiChangeListener {
private static final String TAG = "FolderPickerActivity";
public static final String EXTRA_INITIAL_DIRECTORY = "initial_directory";
public static final String EXTRA_RESULT_DIRECTORY = "result_directory";
private ListView mListView;
private FileAdapter mAdapter;
private File mLocation;
private SyncthingService mSyncthingService;
private final ServiceConnection mSyncthingServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
SyncthingServiceBinder binder = (SyncthingServiceBinder) service;
mSyncthingService = binder.getService();
mSyncthingService.registerOnApiChangeListener(FolderPickerActivity.this);
}
public void onServiceDisconnected(ComponentName className) {
mSyncthingService = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.folder_picker_activity);
mListView = (ListView) findViewById(android.R.id.list);
mListView.setOnItemClickListener(this);
mListView.setEmptyView(findViewById(android.R.id.empty));
mAdapter = new FileAdapter(this);
mListView.setAdapter(mAdapter);
mLocation = new File(getIntent().getStringExtra(EXTRA_INITIAL_DIRECTORY));
refresh();
bindService(new Intent(this, SyncthingService.class),
mSyncthingServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.folder_picker, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.create_folder:
final EditText et = new EditText(this);
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle(R.string.create_folder)
.setView(et)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
createFolder(et.getText().toString());
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialogInterface) {
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
.showSoftInput(et, InputMethodManager.SHOW_IMPLICIT);
}
});
dialog.show();
return true;
case R.id.select:
Intent intent = new Intent()
.putExtra(EXTRA_RESULT_DIRECTORY, mLocation.getAbsolutePath());
setResult(Activity.RESULT_OK, intent);
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Creates a new folder with the given name and enters it.
*/
private void createFolder(String name) {
File newFolder = new File(mLocation, name);
newFolder.mkdir();
mLocation = newFolder;
refresh();
}
/**
* Refreshes the ListView to show the contents of the folder in {@code }mLocation.peek()}.
*/
private void refresh() {
mAdapter.clear();
File[] contents = mLocation.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory();
}
});
Arrays.sort(contents);
for (File f : contents) {
mAdapter.add(f);
}
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
mLocation = mAdapter.getItem(i);
refresh();
}
private class FileAdapter extends ArrayAdapter<File> {
public FileAdapter(Context context) {
super(context, android.R.layout.simple_list_item_1);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
}
TextView title = (TextView) convertView.findViewById(android.R.id.text1);
title.setText(getItem(position).getName());
return convertView;
}
}
@Override
public void onBackPressed() {
if (!mLocation.equals(Environment.getExternalStorageDirectory())) {
mLocation = mLocation.getParentFile();
refresh();
}
else {
setResult(Activity.RESULT_CANCELED);
finish();
}
}
@Override
public void onApiChange(boolean isAvailable) {
if (!isAvailable) {
setResult(Activity.RESULT_CANCELED);
finish();
}
}
}

View file

@ -16,7 +16,6 @@ import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
@ -24,14 +23,10 @@ import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.Tab;
import android.support.v7.app.ActionBar.TabListener;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.OrientationEventListener;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.nutomic.syncthingandroid.R;
@ -243,7 +238,7 @@ public class MainActivity extends ActionBarActivity
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

View file

@ -131,7 +131,7 @@ public class NodeSettingsActivity extends PreferenceActivity implements
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.node_settings_menu, menu);
getMenuInflater().inflate(R.menu.node_settings, menu);
return super.onCreateOptionsMenu(menu);
}

View file

@ -1,5 +1,6 @@
package com.nutomic.syncthingandroid.gui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
@ -7,6 +8,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
@ -33,6 +35,8 @@ public class RepoSettingsActivity extends PreferenceActivity
implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
SyncthingService.OnApiChangeListener {
private static final int DIRECTORY_REQUEST_CODE = 234;
public static final String ACTION_CREATE = "create";
public static final String ACTION_EDIT = "edit";
@ -60,7 +64,7 @@ public class RepoSettingsActivity extends PreferenceActivity
private EditTextPreference mRepoId;
private EditTextPreference mDirectory;
private Preference mDirectory;
private CheckBoxPreference mRepoMaster;
@ -85,8 +89,8 @@ public class RepoSettingsActivity extends PreferenceActivity
mRepoId = (EditTextPreference) findPreference("repo_id");
mRepoId.setOnPreferenceChangeListener(this);
mDirectory = (EditTextPreference) findPreference("directory");
mDirectory.setOnPreferenceChangeListener(this);
mDirectory = findPreference("directory");
mDirectory.setOnPreferenceClickListener(this);
mRepoMaster = (CheckBoxPreference) findPreference("repo_master");
mRepoMaster.setOnPreferenceChangeListener(this);
mNodes = (PreferenceScreen) findPreference("nodes");
@ -131,7 +135,6 @@ public class RepoSettingsActivity extends PreferenceActivity
mRepoId.setText(mRepo.ID);
mRepoId.setSummary(mRepo.ID);
mDirectory.setText(mRepo.Directory);
mDirectory.setSummary(mRepo.Directory);
mRepoMaster.setChecked(mRepo.ReadOnly);
List<RestApi.Node> nodesList = mSyncthingService.getApi().getNodes();
@ -162,7 +165,7 @@ public class RepoSettingsActivity extends PreferenceActivity
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.repo_settings_menu, menu);
getMenuInflater().inflate(R.menu.repo_settings, menu);
return super.onCreateOptionsMenu(menu);
}
@ -262,7 +265,15 @@ public class RepoSettingsActivity extends PreferenceActivity
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference.equals(mDelete)) {
if (preference.equals(mDirectory)) {
Intent intent = new Intent(this, FolderPickerActivity.class)
.putExtra(FolderPickerActivity.EXTRA_INITIAL_DIRECTORY,
(mRepo.Directory.length() != 0)
? mRepo.Directory
: Environment.getExternalStorageDirectory().getAbsolutePath());
startActivityForResult(intent, DIRECTORY_REQUEST_CODE);
}
else if (preference.equals(mDelete)) {
new AlertDialog.Builder(this)
.setMessage(R.string.delete_repo_confirm)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@ -279,6 +290,15 @@ public class RepoSettingsActivity extends PreferenceActivity
return false;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == DIRECTORY_REQUEST_CODE) {
mRepo.Directory = data.getStringExtra(FolderPickerActivity.EXTRA_RESULT_DIRECTORY);
mDirectory.setSummary(mRepo.Directory);
repoUpdated();
}
}
private void repoUpdated() {
if (getIntent().getAction().equals(ACTION_EDIT)) {
mSyncthingService.getApi().editRepo(mRepo, false);

View file

@ -96,16 +96,25 @@ public class ConfigXml {
changed = true;
}
// Set ignorePerms attribute.
NodeList repos = mConfig.getDocumentElement().getElementsByTagName("repository");
for (int i = 0; i < repos.getLength(); i++) {
Element r = (Element) repos.item(i);
// Set ignorePerms attribute.
if (!r.hasAttribute("ignorePerms") ||
!Boolean.parseBoolean(r.getAttribute("ignorePerms"))) {
Log.i(TAG, "Set 'ignorePerms' on repository " + r.getAttribute("id"));
r.setAttribute("ignorePerms", Boolean.toString(true));
changed = true;
}
// Replace /sdcard/ in repository path with proper path.
String dir = r.getAttribute("directory");
if (dir.startsWith("/sdcard")) {
String newDir = dir.replace("/sdcard",
Environment.getExternalStorageDirectory().getAbsolutePath());
r.setAttribute("directory", newDir);
changed = true;
}
}
if (changed) {

View file

@ -23,7 +23,7 @@ public class ReposAdapter extends ArrayAdapter<RestApi.Repo>
private HashMap<String, RestApi.Model> mModels = new HashMap<String, RestApi.Model>();
public ReposAdapter(Context context) {
super(context, R.layout.node_list_item);
super(context, R.layout.repo_list_item);
}
@Override

View file

@ -0,0 +1,17 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/directory_empty"
android:layout_centerInParent="true"/>
</RelativeLayout>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/create_folder"
android:title="Create Folder" />
<item
android:id="@+id/select"
android:title="Select" />
</menu>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="DialogWhenLargeCompat" parent="android:Theme.Holo.DialogWhenLarge" />
<style name="DialogWhenLarge" parent="android:Theme.Holo.DialogWhenLarge" />
</resources>

View file

@ -14,6 +14,7 @@
<!-- MainActivity -->
<!-- Title of the "add repo" menu action -->
<string name="add_repo">Add Repository</string>
@ -63,6 +64,7 @@
<!-- LocalNodeInfoFragment -->
<!-- Same as download_title with a colon and space appended -->
<string name="download_title_colon">Download:\u0020</string>
@ -80,6 +82,7 @@
<!-- RepoSettingsActivity -->
<!-- Setting title -->
<string name="repo_id">Repository ID</string>
@ -97,7 +100,6 @@
<!-- Setting title -->
<string name="keep_versions">Keep Versions</string>
<string name="delete_repo">Delete Repository</string>
<!-- Title for RepoSettingsActivity in create mode -->
@ -120,6 +122,7 @@
<!-- NodeSettingsActivity -->
<!-- Setting title -->
<string name="node_id">Node ID</string>
@ -152,6 +155,7 @@
<!-- WebGuiActivity -->
<!-- Title of the web gui activity -->
<string name="web_gui_title">Web GUI</string>
@ -171,11 +175,11 @@
<string name="welcome_text">Welcome to Syncthing for Android!\n\n\
This app is currently in Alpha state, and you may experience bugs, performance problems or data loss.\n\n\
There is currently no special handling for mobile data, so it may use up your data volume if active.\n\n\
Please report any problems you encounter.
</string>
Please report any problems you encounter.</string>
<!-- SettingsActivity -->
<string name="settings_title">Settings</string>
<!-- Settings item that opens issue tracker -->
@ -190,8 +194,16 @@ Please report any problems you encounter.
<!-- Title of the preference showing upstream version name -->
<string name="syncthing_version_title">Syncthing Version</string>
<!-- FolderPickerAcitivity -->
<string name="directory_empty">Directory is Empty</string>
<string name="create_folder">Create new Folder</string>
<!-- RestApi -->
<!-- Strings representing units for file sizes, from smallest to largest -->
<string-array name="file_size_units">
<item>B</item>
@ -210,4 +222,8 @@ Please report any problems you encounter.
<item>Tb/s</item>
</string-array>
<string name="title_activity_folder_picker">FolderPickerActivity</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
</resources>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyAppCompatDialogTheme" parent="DialogWhenLargeCompat">
<style name="DialogWhenLargeCompat" parent="DialogWhenLarge">
<item name="android:windowIsFloating">false</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowSoftInputMode">stateAlwaysHidden</item>

View file

@ -6,7 +6,7 @@
android:key="repo_id"
android:title="@string/repo_id" />
<EditTextPreference
<Preference
android:key="directory"
android:title="@string/directory" />

View file

@ -8,7 +8,7 @@
android:enabled="false"
style="?android:preferenceInformationStyle" />
<EditTextPreference
<Preference
android:key="directory"
android:title="@string/directory" />