|
@ -27,6 +27,8 @@ repositories {
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'eu.chainfire:libsuperuser:1.0.0.201504231659'
|
compile 'eu.chainfire:libsuperuser:1.0.0.201504231659'
|
||||||
compile 'com.android.support:appcompat-v7:22.2.1'
|
compile 'com.android.support:appcompat-v7:22.2.1'
|
||||||
|
compile 'com.android.support:design:22.2.1'
|
||||||
|
compile 'com.android.support:appcompat-v7:22.2.1'
|
||||||
compile 'com.android.support:support-v4-preferencefragment:1.0.0@aar'
|
compile 'com.android.support:support-v4-preferencefragment:1.0.0@aar'
|
||||||
androidTestCompile 'com.squareup.okhttp:mockwebserver:2.4.0'
|
androidTestCompile 'com.squareup.okhttp:mockwebserver:2.4.0'
|
||||||
}
|
}
|
||||||
|
@ -43,7 +45,7 @@ android {
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.nutomic.syncthingandroid"
|
applicationId "com.nutomic.syncthingandroid"
|
||||||
minSdkVersion 8
|
minSdkVersion 11
|
||||||
targetSdkVersion 22
|
targetSdkVersion 22
|
||||||
versionCode 72
|
versionCode 72
|
||||||
versionName "0.6.6"
|
versionName "0.6.6"
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
package com.nutomic.syncthingandroid.test.activities;
|
package com.nutomic.syncthingandroid.test.activities;
|
||||||
|
|
||||||
import android.test.ActivityInstrumentationTestCase2;
|
import android.test.ActivityInstrumentationTestCase2;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.activities.MainActivity;
|
import com.nutomic.syncthingandroid.activities.MainActivity;
|
||||||
import com.nutomic.syncthingandroid.fragments.DevicesFragment;
|
import com.nutomic.syncthingandroid.fragments.DevicesFragment;
|
||||||
import com.nutomic.syncthingandroid.fragments.FoldersFragment;
|
import com.nutomic.syncthingandroid.fragments.FolderListFragment;
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder;
|
import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder;
|
||||||
import com.nutomic.syncthingandroid.test.MockSyncthingService;
|
import com.nutomic.syncthingandroid.test.MockSyncthingService;
|
||||||
|
|
||||||
|
@ -20,7 +19,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
|
||||||
public void testOnServiceConnected() {
|
public void testOnServiceConnected() {
|
||||||
getActivity().onServiceConnected(null, new SyncthingServiceBinder(mService));
|
getActivity().onServiceConnected(null, new SyncthingServiceBinder(mService));
|
||||||
assertTrue(mService.containsListenerInstance(MainActivity.class));
|
assertTrue(mService.containsListenerInstance(MainActivity.class));
|
||||||
assertTrue(mService.containsListenerInstance(FoldersFragment.class));
|
assertTrue(mService.containsListenerInstance(FolderListFragment.class));
|
||||||
assertTrue(mService.containsListenerInstance(DevicesFragment.class));
|
assertTrue(mService.containsListenerInstance(DevicesFragment.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
package com.nutomic.syncthingandroid.test.util;
|
|
||||||
|
|
||||||
import android.test.AndroidTestCase;
|
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.util.ExtendedCheckBoxPreference;
|
|
||||||
|
|
||||||
public class ExtendedCheckBoxPreferenceTest extends AndroidTestCase {
|
|
||||||
|
|
||||||
public void testExtendedCheckBoxPreference() {
|
|
||||||
Object o = new Object();
|
|
||||||
ExtendedCheckBoxPreference cb = new ExtendedCheckBoxPreference(getContext(), o);
|
|
||||||
assertEquals(cb.getObject(), o);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -61,8 +61,6 @@ public class FolderPickerActivity extends SyncthingActivity
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_folder_picker);
|
setContentView(R.layout.activity_folder_picker);
|
||||||
mListView = (ListView) findViewById(android.R.id.list);
|
mListView = (ListView) findViewById(android.R.id.list);
|
||||||
mListView.setOnItemClickListener(this);
|
mListView.setOnItemClickListener(this);
|
||||||
|
@ -227,6 +225,7 @@ public class FolderPickerActivity extends SyncthingActivity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
ArrayAdapter<File> adapter = (ArrayAdapter<File>) mListView.getAdapter();
|
ArrayAdapter<File> adapter = (ArrayAdapter<File>) mListView.getAdapter();
|
||||||
File f = adapter.getItem(i);
|
File f = adapter.getItem(i);
|
||||||
if (f.isDirectory()) {
|
if (f.isDirectory()) {
|
||||||
|
@ -246,7 +245,7 @@ public class FolderPickerActivity extends SyncthingActivity
|
||||||
private class FileAdapter extends ArrayAdapter<File> {
|
private class FileAdapter extends ArrayAdapter<File> {
|
||||||
|
|
||||||
public FileAdapter(Context context) {
|
public FileAdapter(Context context) {
|
||||||
super(context, android.R.layout.simple_list_item_1);
|
super(context, R.layout.item_folder_picker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -41,7 +41,6 @@ public class LogActivity extends SyncthingActivity {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setContentView(R.layout.activity_log);
|
setContentView(R.layout.activity_log);
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
mSyncthingLog = savedInstanceState.getBoolean("syncthingLog");
|
mSyncthingLog = savedInstanceState.getBoolean("syncthingLog");
|
||||||
|
|
|
@ -13,35 +13,38 @@ import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.design.widget.TabLayout;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.app.FragmentPagerAdapter;
|
import android.support.v4.app.FragmentPagerAdapter;
|
||||||
import android.support.v4.app.FragmentTransaction;
|
import android.support.v4.view.GravityCompat;
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
import android.support.v4.widget.DrawerLayout;
|
import android.support.v4.widget.DrawerLayout;
|
||||||
import android.support.v7.app.ActionBar;
|
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.ActionBarDrawerToggle;
|
import android.support.v7.app.ActionBarDrawerToggle;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Gravity;
|
import android.util.TypedValue;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.R;
|
import com.nutomic.syncthingandroid.R;
|
||||||
import com.nutomic.syncthingandroid.fragments.DevicesFragment;
|
import com.nutomic.syncthingandroid.fragments.DevicesFragment;
|
||||||
import com.nutomic.syncthingandroid.fragments.DrawerFragment;
|
import com.nutomic.syncthingandroid.fragments.DrawerFragment;
|
||||||
import com.nutomic.syncthingandroid.fragments.FoldersFragment;
|
import com.nutomic.syncthingandroid.fragments.FolderListFragment;
|
||||||
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows {@link com.nutomic.syncthingandroid.fragments.FoldersFragment} and
|
* Shows {@link FolderListFragment} and
|
||||||
* {@link com.nutomic.syncthingandroid.fragments.DevicesFragment} in different tabs, and
|
* {@link com.nutomic.syncthingandroid.fragments.DevicesFragment} in different tabs, and
|
||||||
* {@link com.nutomic.syncthingandroid.fragments.DrawerFragment} in the navigation drawer.
|
* {@link com.nutomic.syncthingandroid.fragments.DrawerFragment} in the navigation drawer.
|
||||||
*/
|
*/
|
||||||
|
@ -112,10 +115,9 @@ public class MainActivity extends SyncthingActivity
|
||||||
mDisabledDialog.dismiss();
|
mDisabledDialog.dismiss();
|
||||||
mDisabledDialog = null;
|
mDisabledDialog = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
|
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
|
||||||
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
mDrawerFragment.requestGuiUpdate();
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
getSupportActionBar().setHomeButtonEnabled(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -172,9 +174,20 @@ public class MainActivity extends SyncthingActivity
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getPageTitle(int position) {
|
||||||
|
switch (position) {
|
||||||
|
case 0:
|
||||||
|
return getResources().getString(R.string.folders_fragment_title);
|
||||||
|
case 1:
|
||||||
|
return getResources().getString(R.string.devices_fragment_title);
|
||||||
|
default:
|
||||||
|
return String.valueOf(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private FoldersFragment mFolderFragment;
|
private FolderListFragment mFolderFragment;
|
||||||
|
|
||||||
private DevicesFragment mDevicesFragment;
|
private DevicesFragment mDevicesFragment;
|
||||||
|
|
||||||
|
@ -191,53 +204,27 @@ public class MainActivity extends SyncthingActivity
|
||||||
*/
|
*/
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
final ActionBar actionBar = getSupportActionBar();
|
|
||||||
|
|
||||||
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
|
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
|
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||||
|
|
||||||
mViewPager = (ViewPager) findViewById(R.id.pager);
|
mViewPager = (ViewPager) findViewById(R.id.pager);
|
||||||
mViewPager.setAdapter(mSectionsPagerAdapter);
|
mViewPager.setAdapter(mSectionsPagerAdapter);
|
||||||
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onPageSelected(int position) {
|
|
||||||
actionBar.setSelectedNavigationItem(position);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
TabListener tabListener = new TabListener() {
|
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabContainer);
|
||||||
public void onTabSelected(Tab tab, FragmentTransaction ft) {
|
tabLayout.setupWithViewPager(mViewPager);
|
||||||
mViewPager.setCurrentItem(tab.getPosition());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTabReselected(Tab tab, FragmentTransaction ft) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
actionBar.addTab(actionBar.newTab()
|
|
||||||
.setText(R.string.folders_fragment_title)
|
|
||||||
.setTabListener(tabListener));
|
|
||||||
actionBar.addTab(actionBar.newTab()
|
|
||||||
.setText(R.string.devices_fragment_title)
|
|
||||||
.setTabListener(tabListener));
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
FragmentManager fm = getSupportFragmentManager();
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
mFolderFragment = (FoldersFragment) fm.getFragment(
|
mFolderFragment = (FolderListFragment) fm.getFragment(
|
||||||
savedInstanceState, FoldersFragment.class.getName());
|
savedInstanceState, FolderListFragment.class.getName());
|
||||||
mDevicesFragment = (DevicesFragment) fm.getFragment(
|
mDevicesFragment = (DevicesFragment) fm.getFragment(
|
||||||
savedInstanceState, DevicesFragment.class.getName());
|
savedInstanceState, DevicesFragment.class.getName());
|
||||||
mDrawerFragment = (DrawerFragment) fm.getFragment(
|
mDrawerFragment = (DrawerFragment) fm.getFragment(
|
||||||
savedInstanceState, DrawerFragment.class.getName());
|
savedInstanceState, DrawerFragment.class.getName());
|
||||||
mViewPager.setCurrentItem(savedInstanceState.getInt("currentTab"));
|
mViewPager.setCurrentItem(savedInstanceState.getInt("currentTab"));
|
||||||
} else {
|
} else {
|
||||||
mFolderFragment = new FoldersFragment();
|
mFolderFragment = new FolderListFragment();
|
||||||
mDevicesFragment = new DevicesFragment();
|
mDevicesFragment = new DevicesFragment();
|
||||||
mDrawerFragment = new DrawerFragment();
|
mDrawerFragment = new DrawerFragment();
|
||||||
}
|
}
|
||||||
|
@ -247,7 +234,9 @@ public class MainActivity extends SyncthingActivity
|
||||||
.replace(R.id.drawer, mDrawerFragment)
|
.replace(R.id.drawer, mDrawerFragment)
|
||||||
.commit();
|
.commit();
|
||||||
mDrawerToggle = new Toggle(this, mDrawerLayout);
|
mDrawerToggle = new Toggle(this, mDrawerLayout);
|
||||||
|
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
|
||||||
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
||||||
|
setOptimalDrawerWidth(findViewById(R.id.drawer));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -278,13 +267,11 @@ public class MainActivity extends SyncthingActivity
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
// Avoid crash if called during startup.
|
// Avoid crash if called during startup.
|
||||||
if (mFolderFragment != null && mDevicesFragment != null &&
|
if (mFolderFragment != null && mDevicesFragment != null && mDrawerFragment != null) {
|
||||||
mDrawerFragment != null) {
|
|
||||||
FragmentManager fm = getSupportFragmentManager();
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
fm.putFragment(outState, FoldersFragment.class.getName(), mFolderFragment);
|
fm.putFragment(outState, FolderListFragment.class.getName(), mFolderFragment);
|
||||||
fm.putFragment(outState, DevicesFragment.class.getName(), mDevicesFragment);
|
fm.putFragment(outState, DevicesFragment.class.getName(), mDevicesFragment);
|
||||||
fm.putFragment(outState, DrawerFragment.class.getName(),
|
fm.putFragment(outState, DrawerFragment.class.getName(), mDrawerFragment);
|
||||||
mDrawerFragment);
|
|
||||||
outState.putInt("currentTab", mViewPager.getCurrentItem());
|
outState.putInt("currentTab", mViewPager.getCurrentItem());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,7 +279,13 @@ public class MainActivity extends SyncthingActivity
|
||||||
@Override
|
@Override
|
||||||
protected void onPostCreate(Bundle savedInstanceState) {
|
protected void onPostCreate(Bundle savedInstanceState) {
|
||||||
super.onPostCreate(savedInstanceState);
|
super.onPostCreate(savedInstanceState);
|
||||||
|
|
||||||
mDrawerToggle.syncState();
|
mDrawerToggle.syncState();
|
||||||
|
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setHomeButtonEnabled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -306,7 +299,6 @@ public class MainActivity extends SyncthingActivity
|
||||||
return mDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
|
return mDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles drawer opened and closed events, toggling option menu state.
|
* Handles drawer opened and closed events, toggling option menu state.
|
||||||
*/
|
*/
|
||||||
|
@ -319,16 +311,12 @@ public class MainActivity extends SyncthingActivity
|
||||||
public void onDrawerOpened(View drawerView) {
|
public void onDrawerOpened(View drawerView) {
|
||||||
super.onDrawerOpened(drawerView);
|
super.onDrawerOpened(drawerView);
|
||||||
mDrawerFragment.onDrawerOpened();
|
mDrawerFragment.onDrawerOpened();
|
||||||
mFolderFragment.setHasOptionsMenu(false);
|
|
||||||
mDevicesFragment.setHasOptionsMenu(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDrawerClosed(View view) {
|
public void onDrawerClosed(View view) {
|
||||||
super.onDrawerClosed(view);
|
super.onDrawerClosed(view);
|
||||||
mDrawerFragment.onDrawerClosed();
|
mDrawerFragment.onDrawerClosed();
|
||||||
mFolderFragment.setHasOptionsMenu(true);
|
|
||||||
mDevicesFragment.setHasOptionsMenu(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,7 +324,7 @@ public class MainActivity extends SyncthingActivity
|
||||||
* Closes the drawer. Use when navigating away from activity.
|
* Closes the drawer. Use when navigating away from activity.
|
||||||
*/
|
*/
|
||||||
public void closeDrawer() {
|
public void closeDrawer() {
|
||||||
mDrawerLayout.closeDrawer(Gravity.START);
|
mDrawerLayout.closeDrawer(GravityCompat.START);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -345,8 +333,8 @@ public class MainActivity extends SyncthingActivity
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyDown(int keyCode, KeyEvent e) {
|
public boolean onKeyDown(int keyCode, KeyEvent e) {
|
||||||
if (keyCode == KeyEvent.KEYCODE_MENU) {
|
if (keyCode == KeyEvent.KEYCODE_MENU) {
|
||||||
if (!mDrawerLayout.isDrawerOpen(Gravity.START))
|
if (!mDrawerLayout.isDrawerOpen(GravityCompat.START))
|
||||||
mDrawerLayout.openDrawer(Gravity.START);
|
mDrawerLayout.openDrawer(GravityCompat.START);
|
||||||
else
|
else
|
||||||
closeDrawer();
|
closeDrawer();
|
||||||
|
|
||||||
|
@ -355,6 +343,25 @@ public class MainActivity extends SyncthingActivity
|
||||||
return super.onKeyDown(keyCode, e);
|
return super.onKeyDown(keyCode, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculating width based on
|
||||||
|
* http://www.google.com/design/spec/patterns/navigation-drawer.html#navigation-drawer-specs.
|
||||||
|
*/
|
||||||
|
private void setOptimalDrawerWidth(View drawerContainer) {
|
||||||
|
int actionBarSize = 0;
|
||||||
|
TypedValue tv = new TypedValue();
|
||||||
|
if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
|
||||||
|
actionBarSize = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewGroup.LayoutParams params = drawerContainer.getLayoutParams();
|
||||||
|
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
|
||||||
|
int minScreenWidth = min(displayMetrics.widthPixels, displayMetrics.heightPixels);
|
||||||
|
|
||||||
|
params.width = min(minScreenWidth - actionBarSize, 5 * actionBarSize);
|
||||||
|
drawerContainer.requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays dialog asking user to accept/deny usage reporting.
|
* Displays dialog asking user to accept/deny usage reporting.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.nutomic.syncthingandroid.activities;
|
package com.nutomic.syncthingandroid.activities;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
|
@ -9,12 +8,9 @@ import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.BuildConfig;
|
|
||||||
import com.nutomic.syncthingandroid.R;
|
import com.nutomic.syncthingandroid.R;
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||||
|
|
||||||
|
@ -32,8 +28,6 @@ public class RestartActivity extends SyncthingActivity {
|
||||||
@TargetApi(11)
|
@TargetApi(11)
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
// Using `<item name="windowActionBar">false</item>` in style causes crash.
|
|
||||||
getSupportActionBar().hide();
|
|
||||||
|
|
||||||
final Intent intent = new Intent(this, SyncthingService.class)
|
final Intent intent = new Intent(this, SyncthingService.class)
|
||||||
.setAction(SyncthingService.ACTION_RESTART);
|
.setAction(SyncthingService.ACTION_RESTART);
|
||||||
|
|
|
@ -7,7 +7,7 @@ import android.view.View;
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.R;
|
import com.nutomic.syncthingandroid.R;
|
||||||
import com.nutomic.syncthingandroid.fragments.DeviceSettingsFragment;
|
import com.nutomic.syncthingandroid.fragments.DeviceSettingsFragment;
|
||||||
import com.nutomic.syncthingandroid.fragments.FolderSettingsFragment;
|
import com.nutomic.syncthingandroid.fragments.FolderFragment;
|
||||||
import com.nutomic.syncthingandroid.fragments.SettingsFragment;
|
import com.nutomic.syncthingandroid.fragments.SettingsFragment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,7 +26,7 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
* {@link #ACTION_REPO_SETTINGS_FRAGMENT} to determine if an existing folder/device should be
|
* {@link #ACTION_REPO_SETTINGS_FRAGMENT} to determine if an existing folder/device should be
|
||||||
* edited or a new one created.
|
* edited or a new one created.
|
||||||
* <p/>
|
* <p/>
|
||||||
* If this is false, {@link com.nutomic.syncthingandroid.fragments.FolderSettingsFragment#EXTRA_REPO_ID} or
|
* If this is false, {@link FolderFragment#EXTRA_REPO_ID} or
|
||||||
* {@link com.nutomic.syncthingandroid.fragments.DeviceSettingsFragment#EXTRA_NODE_ID} must be set (according to the selected fragment).
|
* {@link com.nutomic.syncthingandroid.fragments.DeviceSettingsFragment#EXTRA_NODE_ID} must be set (according to the selected fragment).
|
||||||
*/
|
*/
|
||||||
public static final String EXTRA_IS_CREATE = "create";
|
public static final String EXTRA_IS_CREATE = "create";
|
||||||
|
@ -37,7 +37,7 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
setContentView(R.layout.activity_settings);
|
||||||
|
|
||||||
FragmentManager fm = getSupportFragmentManager();
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
|
@ -56,7 +56,7 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ACTION_REPO_SETTINGS_FRAGMENT:
|
case ACTION_REPO_SETTINGS_FRAGMENT:
|
||||||
mFragment = new FolderSettingsFragment();
|
mFragment = new FolderFragment();
|
||||||
if (!getIntent().hasExtra(EXTRA_IS_CREATE)) {
|
if (!getIntent().hasExtra(EXTRA_IS_CREATE)) {
|
||||||
throw new IllegalArgumentException("EXTRA_IS_CREATE must be set");
|
throw new IllegalArgumentException("EXTRA_IS_CREATE must be set");
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
fm.beginTransaction()
|
fm.beginTransaction()
|
||||||
.replace(android.R.id.content, mFragment)
|
.replace(R.id.content, mFragment)
|
||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,6 @@ import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||||
|
@ -18,7 +16,7 @@ import java.util.LinkedList;
|
||||||
/**
|
/**
|
||||||
* Connects to {@link SyncthingService} and provides access to it.
|
* Connects to {@link SyncthingService} and provides access to it.
|
||||||
*/
|
*/
|
||||||
public class SyncthingActivity extends AppCompatActivity implements ServiceConnection {
|
public abstract class SyncthingActivity extends ToolbarBindingActivity implements ServiceConnection {
|
||||||
|
|
||||||
private SyncthingService mSyncthingService;
|
private SyncthingService mSyncthingService;
|
||||||
|
|
||||||
|
@ -28,7 +26,7 @@ public class SyncthingActivity extends AppCompatActivity implements ServiceConne
|
||||||
* To be used for Fragments.
|
* To be used for Fragments.
|
||||||
*/
|
*/
|
||||||
public interface OnServiceConnectedListener {
|
public interface OnServiceConnectedListener {
|
||||||
public void onServiceConnected();
|
void onServiceConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.nutomic.syncthingandroid.activities;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
|
||||||
|
import com.nutomic.syncthingandroid.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An activity that onPostCreate will look for a Toolbar in the layout
|
||||||
|
* and bind it as the activity's actionbar with reasonable defaults. <br/>
|
||||||
|
* The Toolbar must exist in the content view and have an id of R.id.toolbar.<br/>
|
||||||
|
* Trying to call getSupportActionBar before this Activity's onPostCreate will cause a crash.
|
||||||
|
*/
|
||||||
|
public class ToolbarBindingActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostCreate(Bundle savedInstanceState) {
|
||||||
|
super.onPostCreate(savedInstanceState);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
|
if (toolbar != null) {
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,6 @@ import android.webkit.HttpAuthHandler;
|
||||||
import android.webkit.SslErrorHandler;
|
import android.webkit.SslErrorHandler;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
import android.webkit.WebViewClient;
|
import android.webkit.WebViewClient;
|
||||||
import android.widget.ProgressBar;
|
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.R;
|
import com.nutomic.syncthingandroid.R;
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||||
|
@ -105,12 +104,8 @@ public class WebGuiActivity extends SyncthingActivity
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setContentView(R.layout.activity_web_gui);
|
setContentView(R.layout.activity_web_gui);
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
|
|
||||||
mLoadingView = findViewById(R.id.loading);
|
mLoadingView = findViewById(R.id.loading);
|
||||||
ProgressBar pb = (ProgressBar) mLoadingView.findViewById(R.id.progress);
|
|
||||||
pb.setIndeterminate(true);
|
|
||||||
|
|
||||||
loadCaCert();
|
loadCaCert();
|
||||||
|
|
||||||
mWebView = (WebView) findViewById(R.id.webview);
|
mWebView = (WebView) findViewById(R.id.webview);
|
||||||
|
|
|
@ -1,19 +1,12 @@
|
||||||
package com.nutomic.syncthingandroid.fragments;
|
package com.nutomic.syncthingandroid.fragments;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.util.Pair;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.R;
|
import com.nutomic.syncthingandroid.R;
|
||||||
|
@ -24,17 +17,17 @@ import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
|
import static android.content.Intent.ACTION_VIEW;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays information about the local device.
|
* Displays information about the local device.
|
||||||
*/
|
*/
|
||||||
public class DrawerFragment extends Fragment implements RestApi.OnReceiveSystemInfoListener,
|
public class DrawerFragment extends Fragment implements RestApi.OnReceiveSystemInfoListener,
|
||||||
RestApi.OnReceiveConnectionsListener, ListView.OnItemClickListener {
|
RestApi.OnReceiveConnectionsListener, View.OnClickListener {
|
||||||
|
|
||||||
private TextView mDeviceId;
|
private TextView mDeviceId;
|
||||||
|
|
||||||
|
@ -48,37 +41,12 @@ public class DrawerFragment extends Fragment implements RestApi.OnReceiveSystemI
|
||||||
|
|
||||||
private TextView mAnnounceServer;
|
private TextView mAnnounceServer;
|
||||||
|
|
||||||
private ListView mList;
|
private TextView mExitButton;
|
||||||
|
|
||||||
private Timer mTimer;
|
private Timer mTimer;
|
||||||
|
|
||||||
private MainActivity mActivity;
|
private MainActivity mActivity;
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays menu items.
|
|
||||||
*/
|
|
||||||
private class MenuAdapter extends ArrayAdapter<Pair<Integer, Integer>> {
|
|
||||||
|
|
||||||
public MenuAdapter(Context context, List<Pair<Integer, Integer>> items) {
|
|
||||||
super(context, 0, items);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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(R.layout.item_menu, parent, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
|
|
||||||
icon.setImageResource(getItem(position).first);
|
|
||||||
TextView text = (TextView) convertView.findViewById(R.id.text);
|
|
||||||
text.setText(getItem(position).second);
|
|
||||||
return convertView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onDrawerOpened() {
|
public void onDrawerOpened() {
|
||||||
mTimer = new Timer();
|
mTimer = new Timer();
|
||||||
mTimer.schedule(new TimerTask() {
|
mTimer.schedule(new TimerTask() {
|
||||||
|
@ -93,7 +61,7 @@ public class DrawerFragment extends Fragment implements RestApi.OnReceiveSystemI
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
initMenu();
|
updateExitButtonVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDrawerClosed() {
|
public void onDrawerClosed() {
|
||||||
|
@ -114,35 +82,35 @@ public class DrawerFragment extends Fragment implements RestApi.OnReceiveSystemI
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_drawer, container, false);
|
return inflater.inflate(R.layout.fragment_drawer, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
mDeviceId = (TextView) view.findViewById(R.id.device_id);
|
mDeviceId = (TextView) view.findViewById(R.id.device_id);
|
||||||
mCpuUsage = (TextView) view.findViewById(R.id.cpu_usage);
|
mCpuUsage = (TextView) view.findViewById(R.id.cpu_usage);
|
||||||
mRamUsage = (TextView) view.findViewById(R.id.ram_usage);
|
mRamUsage = (TextView) view.findViewById(R.id.ram_usage);
|
||||||
mDownload = (TextView) view.findViewById(R.id.download);
|
mDownload = (TextView) view.findViewById(R.id.download);
|
||||||
mUpload = (TextView) view.findViewById(R.id.upload);
|
mUpload = (TextView) view.findViewById(R.id.upload);
|
||||||
mAnnounceServer = (TextView) view.findViewById(R.id.announce_server);
|
mAnnounceServer = (TextView) view.findViewById(R.id.announce_server);
|
||||||
mList = (ListView) view.findViewById(android.R.id.list);
|
mExitButton = (TextView) view.findViewById(R.id.drawerActionExit);
|
||||||
|
|
||||||
initMenu();
|
view.findViewById(R.id.deviceIdContainer)
|
||||||
mList.setOnItemClickListener(this);
|
.setOnClickListener(this);
|
||||||
|
view.findViewById(R.id.drawerActionWebGui)
|
||||||
|
.setOnClickListener(this);
|
||||||
|
view.findViewById(R.id.drawerActionDonate)
|
||||||
|
.setOnClickListener(this);
|
||||||
|
view.findViewById(R.id.drawerActionSettings)
|
||||||
|
.setOnClickListener(this);
|
||||||
|
mExitButton.setOnClickListener(this);
|
||||||
|
|
||||||
return view;
|
updateExitButtonVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void updateExitButtonVisibility() {
|
||||||
* Repopulates menu items.
|
boolean alwaysInBackground = SyncthingService.alwaysRunInBackground(getActivity());
|
||||||
*/
|
mExitButton.setVisibility(alwaysInBackground ? View.GONE : View.VISIBLE);
|
||||||
private void initMenu() {
|
|
||||||
MenuAdapter adapter =
|
|
||||||
new MenuAdapter(getActivity(), new ArrayList<Pair<Integer, Integer>>());
|
|
||||||
adapter.add(new Pair<>(R.drawable.ic_action_share, R.string.share_device_id));
|
|
||||||
adapter.add(new Pair<>(R.drawable.ic_menu_browser, R.string.web_gui_title));
|
|
||||||
adapter.add(new Pair<>(R.drawable.ic_action_settings, R.string.settings_title));
|
|
||||||
adapter.add(new Pair<>(R.drawable.ic_action_donate, R.string.donate));
|
|
||||||
if (!SyncthingService.alwaysRunInBackground(getActivity()))
|
|
||||||
adapter.add(new Pair<>(R.drawable.ic_menu_close_clear_cancel, R.string.exit));
|
|
||||||
|
|
||||||
mList.setAdapter(adapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -171,6 +139,15 @@ public class DrawerFragment extends Fragment implements RestApi.OnReceiveSystemI
|
||||||
mActivity.getApi().getConnections(this);
|
mActivity.getApi().getConnections(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will not do anything if gui updates are already scheduled.
|
||||||
|
*/
|
||||||
|
public void requestGuiUpdate() {
|
||||||
|
if (mTimer == null) {
|
||||||
|
updateGui();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates views with status received via {@link RestApi#getSystemInfo}.
|
* Populates views with status received via {@link RestApi#getSystemInfo}.
|
||||||
*/
|
*/
|
||||||
|
@ -181,14 +158,10 @@ public class DrawerFragment extends Fragment implements RestApi.OnReceiveSystemI
|
||||||
|
|
||||||
mDeviceId.setText(info.myID);
|
mDeviceId.setText(info.myID);
|
||||||
mDeviceId.setVisibility(View.VISIBLE);
|
mDeviceId.setVisibility(View.VISIBLE);
|
||||||
mDeviceId.setOnTouchListener(new View.OnTouchListener() {
|
mDeviceId.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
public void onClick(View v) {
|
||||||
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
|
mActivity.getApi().copyDeviceId(mDeviceId.getText().toString());
|
||||||
mActivity.getApi().copyDeviceId(mDeviceId.getText().toString());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
mCpuUsage.setText(new DecimalFormat("0.00").format(info.cpuPercent) + "%");
|
mCpuUsage.setText(new DecimalFormat("0.00").format(info.cpuPercent) + "%");
|
||||||
|
@ -212,34 +185,31 @@ public class DrawerFragment extends Fragment implements RestApi.OnReceiveSystemI
|
||||||
mUpload.setText(RestApi.readableTransferRate(mActivity, c.outBits));
|
mUpload.setText(RestApi.readableTransferRate(mActivity, c.outBits));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles menu item clicks.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
public void onClick(View v) {
|
||||||
switch (i) {
|
switch (v.getId()) {
|
||||||
case 0:
|
case R.id.deviceIdContainer:
|
||||||
RestApi.shareDeviceId(getActivity(), mDeviceId.getText().toString());
|
RestApi.shareDeviceId(getActivity(), mDeviceId.getText().toString());
|
||||||
break;
|
return;
|
||||||
case 1:
|
case R.id.drawerActionWebGui:
|
||||||
startActivity(new Intent(mActivity, WebGuiActivity.class));
|
startActivity(new Intent(mActivity, WebGuiActivity.class));
|
||||||
mActivity.closeDrawer();
|
mActivity.closeDrawer();
|
||||||
break;
|
return;
|
||||||
case 2:
|
case R.id.drawerActionDonate:
|
||||||
|
startActivity(new Intent(ACTION_VIEW, Uri.parse(
|
||||||
|
getString(R.string.donate_url))));
|
||||||
|
mActivity.closeDrawer();
|
||||||
|
return;
|
||||||
|
case R.id.drawerActionSettings:
|
||||||
startActivity(new Intent(mActivity, SettingsActivity.class)
|
startActivity(new Intent(mActivity, SettingsActivity.class)
|
||||||
.setAction(SettingsActivity.ACTION_APP_SETTINGS_FRAGMENT));
|
.setAction(SettingsActivity.ACTION_APP_SETTINGS_FRAGMENT));
|
||||||
mActivity.closeDrawer();
|
mActivity.closeDrawer();
|
||||||
break;
|
return;
|
||||||
case 3:
|
case R.id.drawerActionExit:
|
||||||
startActivity(new Intent(
|
|
||||||
Intent.ACTION_VIEW, Uri.parse(getString(R.string.donate_url))));
|
|
||||||
mActivity.closeDrawer();
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
mActivity.stopService(new Intent(mActivity, SyncthingService.class));
|
mActivity.stopService(new Intent(mActivity, SyncthingService.class));
|
||||||
mActivity.finish();
|
mActivity.finish();
|
||||||
mActivity.closeDrawer();
|
mActivity.closeDrawer();
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,403 @@
|
||||||
|
package com.nutomic.syncthingandroid.fragments;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.widget.SwitchCompat;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.nutomic.syncthingandroid.R;
|
||||||
|
import com.nutomic.syncthingandroid.activities.FolderPickerActivity;
|
||||||
|
import com.nutomic.syncthingandroid.activities.SettingsActivity;
|
||||||
|
import com.nutomic.syncthingandroid.activities.SyncthingActivity;
|
||||||
|
import com.nutomic.syncthingandroid.fragments.dialog.KeepVersionsDialogFragment;
|
||||||
|
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||||
|
import com.nutomic.syncthingandroid.syncthing.RestApi.SimpleVersioning;
|
||||||
|
import com.nutomic.syncthingandroid.syncthing.RestApi.Versioning;
|
||||||
|
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static android.support.v4.view.MarginLayoutParamsCompat.setMarginEnd;
|
||||||
|
import static android.support.v4.view.MarginLayoutParamsCompat.setMarginStart;
|
||||||
|
import static android.view.Gravity.CENTER_VERTICAL;
|
||||||
|
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||||
|
import static com.nutomic.syncthingandroid.syncthing.SyncthingService.State.ACTIVE;
|
||||||
|
import static com.nutomic.syncthingandroid.util.DpConverter.dp;
|
||||||
|
import static java.lang.String.valueOf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows folder details and allows changing them.
|
||||||
|
*/
|
||||||
|
public class FolderFragment extends Fragment
|
||||||
|
implements SyncthingActivity.OnServiceConnectedListener, SyncthingService.OnApiChangeListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the folder to be edited. To be used with {@link com.nutomic.syncthingandroid.activities.SettingsActivity#EXTRA_IS_CREATE}
|
||||||
|
* set to false.
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_REPO_ID = "folder_id";
|
||||||
|
|
||||||
|
private static final int DIRECTORY_REQUEST_CODE = 234;
|
||||||
|
|
||||||
|
private static final String TAG = "EditFolderFragment";
|
||||||
|
|
||||||
|
public static final String KEEP_VERSIONS_DIALOG_TAG = "KeepVersionsDialogFragment";
|
||||||
|
|
||||||
|
private SyncthingService mSyncthingService;
|
||||||
|
|
||||||
|
private RestApi.Folder mFolder;
|
||||||
|
|
||||||
|
private EditText mIdView;
|
||||||
|
|
||||||
|
private TextView mPathView;
|
||||||
|
|
||||||
|
private SwitchCompat mFolderMasterView;
|
||||||
|
|
||||||
|
private ViewGroup mDevicesContainer;
|
||||||
|
|
||||||
|
private TextView mVersioningKeepView;
|
||||||
|
|
||||||
|
private boolean mIsCreate;
|
||||||
|
|
||||||
|
private KeepVersionsDialogFragment mKeepVersionsDialogFragment = new KeepVersionsDialogFragment();
|
||||||
|
|
||||||
|
private TextWatcher mIdTextWatcher = new TextWatcherAdapter() {
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
mFolder.id = s.toString();
|
||||||
|
updateRepo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private TextWatcher mPathTextWatcher = new TextWatcherAdapter() {
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
mFolder.path = s.toString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private CompoundButton.OnCheckedChangeListener mMasterCheckedListener = new CompoundButton.OnCheckedChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onCheckedChanged(CompoundButton view, boolean isChecked) {
|
||||||
|
mFolder.readOnly = isChecked;
|
||||||
|
updateRepo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private CompoundButton.OnCheckedChangeListener mOnShareChangeListener = new CompoundButton.OnCheckedChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onCheckedChanged(CompoundButton view, boolean isChecked) {
|
||||||
|
RestApi.Device device = (RestApi.Device) view.getTag();
|
||||||
|
if (isChecked) {
|
||||||
|
mFolder.deviceIds.add(device.deviceID);
|
||||||
|
} else {
|
||||||
|
mFolder.deviceIds.remove(device.deviceID);
|
||||||
|
}
|
||||||
|
updateRepo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private KeepVersionsDialogFragment.OnValueChangeListener mOnValueChangeListener = new KeepVersionsDialogFragment.OnValueChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onValueChange(int intValue) {
|
||||||
|
if (intValue == 0) {
|
||||||
|
mFolder.versioning = new Versioning();
|
||||||
|
mVersioningKeepView.setText(R.string.off);
|
||||||
|
} else {
|
||||||
|
mFolder.versioning = new SimpleVersioning();
|
||||||
|
((SimpleVersioning) mFolder.versioning).setParams(intValue);
|
||||||
|
mVersioningKeepView.setText(valueOf(intValue));
|
||||||
|
}
|
||||||
|
updateRepo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private View.OnClickListener mPathViewClickListener = new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Intent intent = new Intent(getActivity(), FolderPickerActivity.class);
|
||||||
|
if (mFolder.path.length() > 0) {
|
||||||
|
intent.putExtra(FolderPickerActivity.EXTRA_INITIAL_DIRECTORY, mFolder.path);
|
||||||
|
}
|
||||||
|
startActivityForResult(intent, DIRECTORY_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private View.OnClickListener mVersioningContainerClickListener = new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
mKeepVersionsDialogFragment.show(getFragmentManager(), KEEP_VERSIONS_DIALOG_TAG);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
SettingsActivity activity = (SettingsActivity) getActivity();
|
||||||
|
mIsCreate = activity.getIsCreate();
|
||||||
|
activity.setTitle(isCreatingFolder() ? R.string.create_folder : R.string.edit_folder);
|
||||||
|
activity.registerOnServiceConnectedListener(this);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
|
if (isCreatingFolder()) {
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
mFolder = (RestApi.Folder) savedInstanceState.getSerializable("folder");
|
||||||
|
}
|
||||||
|
if (mFolder == null) {
|
||||||
|
initFolder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save current settings in case we are in create mode and they aren't yet stored in the config.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putSerializable("folder", mFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.fragment_folder, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
mIdView = (EditText) view.findViewById(R.id.id);
|
||||||
|
mPathView = (TextView) view.findViewById(R.id.directory);
|
||||||
|
mFolderMasterView = (SwitchCompat) view.findViewById(R.id.master);
|
||||||
|
mVersioningKeepView = (TextView) view.findViewById(R.id.versioningKeep);
|
||||||
|
mDevicesContainer = (ViewGroup) view.findViewById(R.id.devicesContainer);
|
||||||
|
|
||||||
|
mPathView.setOnClickListener(mPathViewClickListener);
|
||||||
|
view.findViewById(R.id.versioningContainer).setOnClickListener(mVersioningContainerClickListener);
|
||||||
|
|
||||||
|
if (isEditingFolder()) {
|
||||||
|
prepareEditMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateViewsAndSetListeners() {
|
||||||
|
// Update views
|
||||||
|
mIdView.setText(mFolder.id);
|
||||||
|
mPathView.setText(mFolder.path);
|
||||||
|
mFolderMasterView.setChecked(mFolder.readOnly);
|
||||||
|
List<RestApi.Device> devicesList = mSyncthingService.getApi().getDevices(false);
|
||||||
|
|
||||||
|
if (devicesList.isEmpty()) {
|
||||||
|
addEmptyDeviceListView();
|
||||||
|
} else {
|
||||||
|
for (RestApi.Device n : devicesList) {
|
||||||
|
addDeviceViewAndSetListener(n, getLayoutInflater(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean versioningEnabled = mFolder.versioning instanceof SimpleVersioning;
|
||||||
|
int versions = 0;
|
||||||
|
if (versioningEnabled) {
|
||||||
|
versions = Integer.valueOf(mFolder.versioning.getParams().get("keep"));
|
||||||
|
mVersioningKeepView.setText(valueOf(versions));
|
||||||
|
} else {
|
||||||
|
mVersioningKeepView.setText(R.string.off);
|
||||||
|
}
|
||||||
|
mKeepVersionsDialogFragment.setValue(versions);
|
||||||
|
|
||||||
|
// Keep state updated
|
||||||
|
mIdView.addTextChangedListener(mIdTextWatcher);
|
||||||
|
mPathView.addTextChangedListener(mPathTextWatcher);
|
||||||
|
mFolderMasterView.setOnCheckedChangeListener(mMasterCheckedListener);
|
||||||
|
mKeepVersionsDialogFragment.setOnValueChangeListener(mOnValueChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
mIdView.removeTextChangedListener(mIdTextWatcher);
|
||||||
|
mPathView.removeTextChangedListener(mPathTextWatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApiChange(SyncthingService.State currentState) {
|
||||||
|
if (currentState != ACTIVE) {
|
||||||
|
getActivity().finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEditingFolder()) {
|
||||||
|
List<RestApi.Folder> folders = mSyncthingService.getApi().getFolders();
|
||||||
|
String passedId = getActivity().getIntent().getStringExtra(EXTRA_REPO_ID);
|
||||||
|
for (RestApi.Folder currentFolder : folders) {
|
||||||
|
if (currentFolder.id.equals(passedId)) {
|
||||||
|
mFolder = currentFolder;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mFolder == null) {
|
||||||
|
Log.w(TAG, "Folder not found in API update");
|
||||||
|
getActivity().finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateViewsAndSetListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected() {
|
||||||
|
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
|
||||||
|
mSyncthingService.registerOnApiChangeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (mSyncthingService != null) {
|
||||||
|
mSyncthingService.unregisterOnApiChangeListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
inflater.inflate(R.menu.folder_settings, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepareOptionsMenu(Menu menu) {
|
||||||
|
menu.findItem(R.id.create).setVisible(isCreatingFolder());
|
||||||
|
menu.findItem(R.id.delete).setVisible(isEditingFolder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.create:
|
||||||
|
if (mFolder.id.length() > 64 || !mFolder.id.matches("[a-zA-Z0-9-_\\.]+")) {
|
||||||
|
Toast.makeText(getActivity(), R.string.folder_id_invalid, Toast.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (mFolder.path.equals("")) {
|
||||||
|
Toast.makeText(getActivity(), R.string.folder_path_required, Toast.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
mSyncthingService.getApi().editFolder(mFolder, true, getActivity());
|
||||||
|
getActivity().finish();
|
||||||
|
return true;
|
||||||
|
case R.id.delete:
|
||||||
|
new AlertDialog.Builder(getActivity())
|
||||||
|
.setMessage(R.string.delete_folder_confirm)
|
||||||
|
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
mSyncthingService.getApi().deleteFolder(mFolder, getActivity());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton(android.R.string.no, null)
|
||||||
|
.show();
|
||||||
|
return true;
|
||||||
|
case android.R.id.home:
|
||||||
|
getActivity().finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
if (resultCode == Activity.RESULT_OK && requestCode == DIRECTORY_REQUEST_CODE) {
|
||||||
|
mFolder.path = data.getStringExtra(FolderPickerActivity.EXTRA_RESULT_DIRECTORY);
|
||||||
|
mPathView.setText(mFolder.path);
|
||||||
|
updateRepo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initFolder() {
|
||||||
|
mFolder = new RestApi.Folder();
|
||||||
|
mFolder.id = "";
|
||||||
|
mFolder.path = "";
|
||||||
|
mFolder.rescanIntervalS = 259200; // Scan every 3 days (in case inotify dropped some changes)
|
||||||
|
mFolder.deviceIds = new ArrayList<>();
|
||||||
|
mFolder.versioning = new Versioning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareEditMode() {
|
||||||
|
mIdView.clearFocus();
|
||||||
|
mIdView.setFocusable(false);
|
||||||
|
mIdView.setEnabled(false);
|
||||||
|
mPathView.setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEmptyDeviceListView() {
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(WRAP_CONTENT, dp(48, getActivity()));
|
||||||
|
// 72dp margin to align with dividers
|
||||||
|
int dividerInset = getResources().getDimensionPixelOffset(R.dimen.material_divider_inset);
|
||||||
|
int contentInset = getResources().getDimensionPixelOffset(R.dimen.abc_action_bar_content_inset_material);
|
||||||
|
setMarginStart(params, dividerInset);
|
||||||
|
setMarginEnd(params, contentInset);
|
||||||
|
TextView emptyView = new TextView(mDevicesContainer.getContext());
|
||||||
|
emptyView.setGravity(CENTER_VERTICAL);
|
||||||
|
emptyView.setText(R.string.devices_list_empty);
|
||||||
|
mDevicesContainer.addView(emptyView, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDeviceViewAndSetListener(RestApi.Device device, LayoutInflater inflater) {
|
||||||
|
inflater.inflate(R.layout.item_device_form, mDevicesContainer);
|
||||||
|
SwitchCompat deviceView = (SwitchCompat) mDevicesContainer.getChildAt(mDevicesContainer.getChildCount()-1);
|
||||||
|
deviceView.setChecked(mFolder.deviceIds.contains(device.deviceID));
|
||||||
|
deviceView.setText(device.name);
|
||||||
|
deviceView.setTag(device);
|
||||||
|
deviceView.setOnCheckedChangeListener(mOnShareChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateRepo() {
|
||||||
|
if (isEditingFolder()) {
|
||||||
|
mSyncthingService.getApi().editFolder(mFolder, false, getActivity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEditingFolder() {
|
||||||
|
return !mIsCreate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCreatingFolder() {
|
||||||
|
return mIsCreate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TextWatcherAdapter implements TextWatcher {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,7 @@ import java.util.TimerTask;
|
||||||
/**
|
/**
|
||||||
* Displays a list of all existing folders.
|
* Displays a list of all existing folders.
|
||||||
*/
|
*/
|
||||||
public class FoldersFragment extends ListFragment implements SyncthingService.OnApiChangeListener,
|
public class FolderListFragment extends ListFragment implements SyncthingService.OnApiChangeListener,
|
||||||
AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {
|
AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {
|
||||||
|
|
||||||
private FoldersAdapter mAdapter;
|
private FoldersAdapter mAdapter;
|
||||||
|
@ -103,7 +103,7 @@ public class FoldersFragment extends ListFragment implements SyncthingService.On
|
||||||
Intent intent = new Intent(getActivity(), SettingsActivity.class)
|
Intent intent = new Intent(getActivity(), SettingsActivity.class)
|
||||||
.setAction(SettingsActivity.ACTION_REPO_SETTINGS_FRAGMENT)
|
.setAction(SettingsActivity.ACTION_REPO_SETTINGS_FRAGMENT)
|
||||||
.putExtra(SettingsActivity.EXTRA_IS_CREATE, false)
|
.putExtra(SettingsActivity.EXTRA_IS_CREATE, false)
|
||||||
.putExtra(FolderSettingsFragment.EXTRA_REPO_ID, mAdapter.getItem(i).id);
|
.putExtra(FolderFragment.EXTRA_REPO_ID, mAdapter.getItem(i).id);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,341 +0,0 @@
|
||||||
package com.nutomic.syncthingandroid.fragments;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.preference.CheckBoxPreference;
|
|
||||||
import android.preference.EditTextPreference;
|
|
||||||
import android.preference.Preference;
|
|
||||||
import android.preference.PreferenceScreen;
|
|
||||||
import android.support.v4.preference.PreferenceFragment;
|
|
||||||
import android.text.InputType;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.R;
|
|
||||||
import com.nutomic.syncthingandroid.activities.FolderPickerActivity;
|
|
||||||
import com.nutomic.syncthingandroid.activities.SettingsActivity;
|
|
||||||
import com.nutomic.syncthingandroid.activities.SyncthingActivity;
|
|
||||||
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
|
||||||
import com.nutomic.syncthingandroid.util.ExtendedCheckBoxPreference;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows folder details and allows changing them.
|
|
||||||
*/
|
|
||||||
public class FolderSettingsFragment extends PreferenceFragment
|
|
||||||
implements SyncthingActivity.OnServiceConnectedListener,
|
|
||||||
Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
|
|
||||||
SyncthingService.OnApiChangeListener {
|
|
||||||
|
|
||||||
private static final int DIRECTORY_REQUEST_CODE = 234;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The ID of the folder to be edited. To be used with {@link com.nutomic.syncthingandroid.activities.SettingsActivity#EXTRA_IS_CREATE}
|
|
||||||
* set to false.
|
|
||||||
*/
|
|
||||||
public static final String EXTRA_REPO_ID = "folder_id";
|
|
||||||
|
|
||||||
private static final String TAG = "FolderSettingsFragment";
|
|
||||||
|
|
||||||
private static final String KEY_NODE_SHARED = "device_shared";
|
|
||||||
|
|
||||||
private SyncthingService mSyncthingService;
|
|
||||||
|
|
||||||
private RestApi.Folder mFolder;
|
|
||||||
|
|
||||||
private EditTextPreference mFolderId;
|
|
||||||
|
|
||||||
private Preference mDirectory;
|
|
||||||
|
|
||||||
private CheckBoxPreference mFolderMaster;
|
|
||||||
|
|
||||||
private PreferenceScreen mDevices;
|
|
||||||
|
|
||||||
private CheckBoxPreference mVersioning;
|
|
||||||
|
|
||||||
private EditTextPreference mVersioningKeep;
|
|
||||||
|
|
||||||
private boolean mIsCreate;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
SettingsActivity activity = (SettingsActivity) getActivity();
|
|
||||||
activity.registerOnServiceConnectedListener(this);
|
|
||||||
mIsCreate = activity.getIsCreate();
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
|
|
||||||
if (mIsCreate) {
|
|
||||||
addPreferencesFromResource(R.xml.folder_settings_create);
|
|
||||||
} else {
|
|
||||||
addPreferencesFromResource(R.xml.folder_settings_edit);
|
|
||||||
}
|
|
||||||
|
|
||||||
mFolderId = (EditTextPreference) findPreference("folder_id");
|
|
||||||
mFolderId.setOnPreferenceChangeListener(this);
|
|
||||||
mDirectory = findPreference("directory");
|
|
||||||
mDirectory.setOnPreferenceClickListener(this);
|
|
||||||
mFolderMaster = (CheckBoxPreference) findPreference("folder_master");
|
|
||||||
mFolderMaster.setOnPreferenceChangeListener(this);
|
|
||||||
mDevices = (PreferenceScreen) findPreference("devices");
|
|
||||||
mDevices.setOnPreferenceClickListener(this);
|
|
||||||
mVersioning = (CheckBoxPreference) findPreference("versioning");
|
|
||||||
mVersioning.setOnPreferenceChangeListener(this);
|
|
||||||
mVersioningKeep = (EditTextPreference) findPreference("versioning_keep");
|
|
||||||
mVersioningKeep.setOnPreferenceChangeListener(this);
|
|
||||||
|
|
||||||
if (mIsCreate) {
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
mFolder = (RestApi.Folder) savedInstanceState.getSerializable("folder");
|
|
||||||
}
|
|
||||||
if (mFolder == null) {
|
|
||||||
mFolder = new RestApi.Folder();
|
|
||||||
mFolder.id = "";
|
|
||||||
mFolder.path = "";
|
|
||||||
mFolder.rescanIntervalS = 259200; // Scan every 3 days (in case inotify dropped some changes)
|
|
||||||
mFolder.deviceIds = new ArrayList<>();
|
|
||||||
mFolder.versioning = new RestApi.Versioning();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save current settings in case we are in create mode and they aren't yet stored in the config.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putSerializable("folder", mFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onApiChange(SyncthingService.State currentState) {
|
|
||||||
if (currentState != SyncthingService.State.ACTIVE) {
|
|
||||||
getActivity().finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mIsCreate) {
|
|
||||||
getActivity().setTitle(R.string.create_folder);
|
|
||||||
} else {
|
|
||||||
RestApi.Folder folder = null;
|
|
||||||
getActivity().setTitle(R.string.edit_folder);
|
|
||||||
List<RestApi.Folder> folders = mSyncthingService.getApi().getFolders();
|
|
||||||
for (int i = 0; i < folders.size(); i++) {
|
|
||||||
if (folders.get(i).id.equals(
|
|
||||||
getActivity().getIntent().getStringExtra(EXTRA_REPO_ID))) {
|
|
||||||
folder = folders.get(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (folder == null) {
|
|
||||||
Log.w(TAG, "Folder not found in API update");
|
|
||||||
getActivity().finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mFolder = folder;
|
|
||||||
}
|
|
||||||
|
|
||||||
mFolderId.setText(mFolder.id);
|
|
||||||
mFolderId.setSummary(mFolder.id);
|
|
||||||
mDirectory.setSummary(mFolder.path);
|
|
||||||
mFolderMaster.setChecked(mFolder.readOnly);
|
|
||||||
List<RestApi.Device> devicesList = mSyncthingService.getApi().getDevices(false);
|
|
||||||
for (RestApi.Device n : devicesList) {
|
|
||||||
ExtendedCheckBoxPreference cbp = new ExtendedCheckBoxPreference(getActivity(), n);
|
|
||||||
// Calling addPreference later causes it to change the checked state.
|
|
||||||
mDevices.addPreference(cbp);
|
|
||||||
cbp.setTitle(n.name);
|
|
||||||
cbp.setKey(KEY_NODE_SHARED);
|
|
||||||
cbp.setOnPreferenceChangeListener(FolderSettingsFragment.this);
|
|
||||||
cbp.setChecked(false);
|
|
||||||
for (String n2 : mFolder.deviceIds) {
|
|
||||||
if (n2.equals(n.deviceID)) {
|
|
||||||
cbp.setChecked(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mVersioning.setChecked(mFolder.versioning instanceof RestApi.SimpleVersioning);
|
|
||||||
if (mVersioning.isChecked()) {
|
|
||||||
mVersioningKeep.setText(mFolder.versioning.getParams().get("keep"));
|
|
||||||
mVersioningKeep.setSummary(mFolder.versioning.getParams().get("keep"));
|
|
||||||
mVersioningKeep.setEnabled(true);
|
|
||||||
} else {
|
|
||||||
mVersioningKeep.setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected() {
|
|
||||||
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
|
|
||||||
mSyncthingService.registerOnApiChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
mSyncthingService.unregisterOnApiChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
inflater.inflate(R.menu.folder_settings, menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPrepareOptionsMenu(Menu menu) {
|
|
||||||
menu.findItem(R.id.create).setVisible(mIsCreate);
|
|
||||||
menu.findItem(R.id.delete).setVisible(!mIsCreate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.create:
|
|
||||||
if (mFolder.id.length() > 64 || !mFolder.id.matches("[a-zA-Z0-9-_\\.]+")) {
|
|
||||||
Toast.makeText(getActivity(), R.string.folder_id_invalid, Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (mFolder.path.equals("")) {
|
|
||||||
Toast.makeText(getActivity(), R.string.folder_path_required, Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
mSyncthingService.getApi().editFolder(mFolder, true, getActivity());
|
|
||||||
getActivity().finish();
|
|
||||||
return true;
|
|
||||||
case R.id.delete:
|
|
||||||
new AlertDialog.Builder(getActivity())
|
|
||||||
.setMessage(R.string.delete_folder_confirm)
|
|
||||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
mSyncthingService.getApi().deleteFolder(mFolder, getActivity());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNegativeButton(android.R.string.no, null)
|
|
||||||
.show();
|
|
||||||
return true;
|
|
||||||
case android.R.id.home:
|
|
||||||
getActivity().finish();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object o) {
|
|
||||||
if (preference instanceof EditTextPreference) {
|
|
||||||
EditTextPreference pref = (EditTextPreference) preference;
|
|
||||||
if ((pref.getEditText().getInputType() & InputType.TYPE_CLASS_NUMBER) > 0) {
|
|
||||||
try {
|
|
||||||
o = Integer.parseInt((String) o);
|
|
||||||
o = o.toString();
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
Log.w(TAG, "Invalid number: " + o);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pref.setSummary((String) o);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preference.equals(mFolderId)) {
|
|
||||||
mFolder.id = (String) o;
|
|
||||||
folderUpdated();
|
|
||||||
return true;
|
|
||||||
} else if (preference.equals(mDirectory)) {
|
|
||||||
mFolder.path = (String) o;
|
|
||||||
folderUpdated();
|
|
||||||
return true;
|
|
||||||
} else if (preference.equals(mFolderMaster)) {
|
|
||||||
mFolder.readOnly = (Boolean) o;
|
|
||||||
folderUpdated();
|
|
||||||
return true;
|
|
||||||
} else if (preference.getKey().equals(KEY_NODE_SHARED)) {
|
|
||||||
ExtendedCheckBoxPreference pref = (ExtendedCheckBoxPreference) preference;
|
|
||||||
RestApi.Device device = (RestApi.Device) pref.getObject();
|
|
||||||
if ((Boolean) o) {
|
|
||||||
mFolder.deviceIds.add(device.deviceID);
|
|
||||||
} else {
|
|
||||||
Iterator<String> it = mFolder.deviceIds.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
String n = it.next();
|
|
||||||
if (n.equals(device.deviceID)) {
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
folderUpdated();
|
|
||||||
return true;
|
|
||||||
} else if (preference.equals(mVersioning)) {
|
|
||||||
mVersioningKeep.setEnabled((Boolean) o);
|
|
||||||
if ((Boolean) o) {
|
|
||||||
RestApi.SimpleVersioning v = new RestApi.SimpleVersioning();
|
|
||||||
mFolder.versioning = v;
|
|
||||||
v.setParams(5);
|
|
||||||
mVersioningKeep.setText("5");
|
|
||||||
mVersioningKeep.setSummary("5");
|
|
||||||
} else {
|
|
||||||
mFolder.versioning = new RestApi.Versioning();
|
|
||||||
}
|
|
||||||
folderUpdated();
|
|
||||||
return true;
|
|
||||||
} else if (preference.equals(mVersioningKeep)) {
|
|
||||||
try {
|
|
||||||
((RestApi.SimpleVersioning) mFolder.versioning)
|
|
||||||
.setParams(Integer.parseInt((String) o));
|
|
||||||
folderUpdated();
|
|
||||||
return true;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
Log.w(TAG, "invalid versioning option: "+ o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
|
||||||
if (preference.equals(mDirectory)) {
|
|
||||||
Intent intent = new Intent(getActivity(), FolderPickerActivity.class);
|
|
||||||
if (mFolder.path.length() > 0) {
|
|
||||||
intent.putExtra(FolderPickerActivity.EXTRA_INITIAL_DIRECTORY, mFolder.path);
|
|
||||||
}
|
|
||||||
startActivityForResult(intent, DIRECTORY_REQUEST_CODE);
|
|
||||||
} else if (preference.equals(mDevices) &&
|
|
||||||
mSyncthingService.getApi().getDevices(false).isEmpty()) {
|
|
||||||
Toast.makeText(getActivity(), R.string.no_devices, Toast.LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
||||||
if (resultCode == Activity.RESULT_OK && requestCode == DIRECTORY_REQUEST_CODE) {
|
|
||||||
mFolder.path = data.getStringExtra(FolderPickerActivity.EXTRA_RESULT_DIRECTORY);
|
|
||||||
mDirectory.setSummary(mFolder.path);
|
|
||||||
folderUpdated();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void folderUpdated() {
|
|
||||||
if (!mIsCreate) {
|
|
||||||
mSyncthingService.getApi().editFolder(mFolder, false, getActivity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
package com.nutomic.syncthingandroid.fragments.dialog;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.widget.FrameLayout.LayoutParams;
|
||||||
|
import android.widget.NumberPicker;
|
||||||
|
|
||||||
|
import com.nutomic.syncthingandroid.R;
|
||||||
|
|
||||||
|
import static android.view.Gravity.CENTER;
|
||||||
|
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||||
|
|
||||||
|
public class KeepVersionsDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
|
private OnValueChangeListener mOnValueChangeListener = OnValueChangeListener.NO_OP;
|
||||||
|
|
||||||
|
private NumberPicker mNumberPickerView;
|
||||||
|
|
||||||
|
private int mValue;
|
||||||
|
|
||||||
|
private final DialogInterface.OnClickListener mDialogButtonListener = new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
switch (which) {
|
||||||
|
case DialogInterface.BUTTON_POSITIVE:
|
||||||
|
mValue = mNumberPickerView.getValue();
|
||||||
|
mOnValueChangeListener.onValueChange(mValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
mNumberPickerView = createNumberPicker();
|
||||||
|
return new AlertDialog.Builder(getActivity())
|
||||||
|
.setTitle(R.string.keep_versions)
|
||||||
|
.setView(mNumberPickerView)
|
||||||
|
.setPositiveButton(android.R.string.ok, mDialogButtonListener)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnValueChangeListener(OnValueChangeListener onValueChangeListener) {
|
||||||
|
if (onValueChangeListener == null) {
|
||||||
|
onValueChangeListener = OnValueChangeListener.NO_OP;
|
||||||
|
}
|
||||||
|
|
||||||
|
mOnValueChangeListener = onValueChangeListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(int value) {
|
||||||
|
this.mValue = value;
|
||||||
|
|
||||||
|
if (mNumberPickerView != null) {
|
||||||
|
mNumberPickerView.setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private NumberPicker createNumberPicker() {
|
||||||
|
NumberPicker picker = new NumberPicker(getActivity());
|
||||||
|
picker.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, CENTER));
|
||||||
|
picker.setMinValue(0);
|
||||||
|
picker.setMaxValue(5);
|
||||||
|
picker.setValue(mValue);
|
||||||
|
picker.setWrapSelectorWheel(false);
|
||||||
|
return picker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnValueChangeListener {
|
||||||
|
OnValueChangeListener NO_OP = new OnValueChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onValueChange(int value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void onValueChange(int value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.nutomic.syncthingandroid.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
|
||||||
|
import static android.util.TypedValue.applyDimension;
|
||||||
|
|
||||||
|
public abstract class DpConverter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts dips to pixels.
|
||||||
|
*
|
||||||
|
* @param dp Number of dps
|
||||||
|
* @param c The context to convert in.
|
||||||
|
* @return Number of pixels that equal dp in context.
|
||||||
|
*/
|
||||||
|
public static int dp(int dp, Context c) {
|
||||||
|
return (int) applyDimension(COMPLEX_UNIT_DIP, dp, c.getResources().getDisplayMetrics());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
package com.nutomic.syncthingandroid.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.preference.CheckBoxPreference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves an extra object on construction, which can be retrieved later.
|
|
||||||
*/
|
|
||||||
public class ExtendedCheckBoxPreference extends CheckBoxPreference {
|
|
||||||
|
|
||||||
private final Object mObject;
|
|
||||||
|
|
||||||
public ExtendedCheckBoxPreference(Context context, Object object) {
|
|
||||||
super(context);
|
|
||||||
mObject = object;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getObject() {
|
|
||||||
return mObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Before Width: | Height: | Size: 957 B |
Before Width: | Height: | Size: 953 B |
Before Width: | Height: | Size: 647 B |
BIN
src/main/res/drawable-hdpi/ic_add_folder_24dp_white.png
Normal file
After Width: | Height: | Size: 197 B |
BIN
src/main/res/drawable-hdpi/ic_add_white_24dp_active.png
Normal file
After Width: | Height: | Size: 124 B |
BIN
src/main/res/drawable-hdpi/ic_attach_money_black_24dp_active.png
Normal file
After Width: | Height: | Size: 396 B |
After Width: | Height: | Size: 364 B |
BIN
src/main/res/drawable-hdpi/ic_close_black_24dp_active.png
Normal file
After Width: | Height: | Size: 224 B |
BIN
src/main/res/drawable-hdpi/ic_close_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 201 B |
Before Width: | Height: | Size: 450 B |
BIN
src/main/res/drawable-hdpi/ic_delete_white_24dp_active.png
Normal file
After Width: | Height: | Size: 161 B |
BIN
src/main/res/drawable-hdpi/ic_device_hub_black_24dp_active.png
Normal file
After Width: | Height: | Size: 263 B |
BIN
src/main/res/drawable-hdpi/ic_device_hub_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 252 B |
BIN
src/main/res/drawable-hdpi/ic_folder_black_24dp_active.png
Normal file
After Width: | Height: | Size: 143 B |
BIN
src/main/res/drawable-hdpi/ic_folder_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 143 B |
BIN
src/main/res/drawable-hdpi/ic_history_black_24dp_active.png
Normal file
After Width: | Height: | Size: 491 B |
BIN
src/main/res/drawable-hdpi/ic_history_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 447 B |
BIN
src/main/res/drawable-hdpi/ic_lock_black_24dp_active.png
Normal file
After Width: | Height: | Size: 327 B |
BIN
src/main/res/drawable-hdpi/ic_lock_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 294 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB |
BIN
src/main/res/drawable-hdpi/ic_save_white_24dp_active.png
Normal file
After Width: | Height: | Size: 237 B |
BIN
src/main/res/drawable-hdpi/ic_settings_black_24dp_active.png
Normal file
After Width: | Height: | Size: 437 B |
BIN
src/main/res/drawable-hdpi/ic_settings_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 394 B |
BIN
src/main/res/drawable-hdpi/ic_share_white_24dp_active.png
Normal file
After Width: | Height: | Size: 390 B |
BIN
src/main/res/drawable-hdpi/ic_vpn_key_black_24dp_active.png
Normal file
After Width: | Height: | Size: 287 B |
BIN
src/main/res/drawable-hdpi/ic_vpn_key_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 255 B |
BIN
src/main/res/drawable-hdpi/ic_web_black_24dp_active.png
Normal file
After Width: | Height: | Size: 171 B |
BIN
src/main/res/drawable-hdpi/ic_web_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 167 B |
Before Width: | Height: | Size: 635 B |
Before Width: | Height: | Size: 594 B |
Before Width: | Height: | Size: 472 B |
Before Width: | Height: | Size: 324 B |
Before Width: | Height: | Size: 960 B |
Before Width: | Height: | Size: 932 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 785 B |
BIN
src/main/res/drawable-xhdpi/ic_add_folder_24dp_white.png
Normal file
After Width: | Height: | Size: 227 B |
BIN
src/main/res/drawable-xhdpi/ic_add_white_24dp_active.png
Normal file
After Width: | Height: | Size: 97 B |
After Width: | Height: | Size: 426 B |
After Width: | Height: | Size: 393 B |
BIN
src/main/res/drawable-xhdpi/ic_close_black_24dp_active.png
Normal file
After Width: | Height: | Size: 250 B |
BIN
src/main/res/drawable-xhdpi/ic_close_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 234 B |
Before Width: | Height: | Size: 543 B |
BIN
src/main/res/drawable-xhdpi/ic_delete_white_24dp_active.png
Normal file
After Width: | Height: | Size: 151 B |
BIN
src/main/res/drawable-xhdpi/ic_device_hub_black_24dp_active.png
Normal file
After Width: | Height: | Size: 314 B |
After Width: | Height: | Size: 297 B |
BIN
src/main/res/drawable-xhdpi/ic_folder_black_24dp_active.png
Normal file
After Width: | Height: | Size: 196 B |
BIN
src/main/res/drawable-xhdpi/ic_folder_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 186 B |
BIN
src/main/res/drawable-xhdpi/ic_history_black_24dp_active.png
Normal file
After Width: | Height: | Size: 627 B |
BIN
src/main/res/drawable-xhdpi/ic_history_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 551 B |
BIN
src/main/res/drawable-xhdpi/ic_lock_black_24dp_active.png
Normal file
After Width: | Height: | Size: 381 B |
BIN
src/main/res/drawable-xhdpi/ic_lock_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 350 B |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.7 KiB |
BIN
src/main/res/drawable-xhdpi/ic_save_white_24dp_active.png
Normal file
After Width: | Height: | Size: 270 B |
BIN
src/main/res/drawable-xhdpi/ic_settings_black_24dp_active.png
Normal file
After Width: | Height: | Size: 566 B |
BIN
src/main/res/drawable-xhdpi/ic_settings_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 514 B |
BIN
src/main/res/drawable-xhdpi/ic_share_white_24dp_active.png
Normal file
After Width: | Height: | Size: 484 B |
BIN
src/main/res/drawable-xhdpi/ic_vpn_key_black_24dp_active.png
Normal file
After Width: | Height: | Size: 346 B |
BIN
src/main/res/drawable-xhdpi/ic_vpn_key_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 316 B |
BIN
src/main/res/drawable-xhdpi/ic_web_black_24dp_active.png
Normal file
After Width: | Height: | Size: 196 B |
BIN
src/main/res/drawable-xhdpi/ic_web_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 182 B |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.1 KiB |
BIN
src/main/res/drawable-xxhdpi/ic_add_folder_24dp_white.png
Normal file
After Width: | Height: | Size: 281 B |
BIN
src/main/res/drawable-xxhdpi/ic_add_white_24dp_active.png
Normal file
After Width: | Height: | Size: 97 B |
After Width: | Height: | Size: 633 B |
After Width: | Height: | Size: 580 B |
BIN
src/main/res/drawable-xxhdpi/ic_close_black_24dp_active.png
Normal file
After Width: | Height: | Size: 328 B |
BIN
src/main/res/drawable-xxhdpi/ic_close_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 305 B |
Before Width: | Height: | Size: 765 B |
BIN
src/main/res/drawable-xxhdpi/ic_delete_white_24dp_active.png
Normal file
After Width: | Height: | Size: 193 B |
BIN
src/main/res/drawable-xxhdpi/ic_device_hub_black_24dp_active.png
Normal file
After Width: | Height: | Size: 430 B |
After Width: | Height: | Size: 408 B |
BIN
src/main/res/drawable-xxhdpi/ic_folder_black_24dp_active.png
Normal file
After Width: | Height: | Size: 277 B |
BIN
src/main/res/drawable-xxhdpi/ic_folder_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 266 B |
BIN
src/main/res/drawable-xxhdpi/ic_history_black_24dp_active.png
Normal file
After Width: | Height: | Size: 897 B |
BIN
src/main/res/drawable-xxhdpi/ic_history_black_24dp_inactive.png
Normal file
After Width: | Height: | Size: 822 B |
BIN
src/main/res/drawable-xxhdpi/ic_lock_black_24dp_active.png
Normal file
After Width: | Height: | Size: 562 B |