mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-12-23 11:21:29 +00:00
Show dialog for usage reporting after some time (fixes #273).
This commit is contained in:
parent
9a587505be
commit
c69b37bc5c
9 changed files with 240 additions and 72 deletions
|
@ -1,12 +1,15 @@
|
|||
package com.nutomic.syncthingandroid.activities;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ComponentName;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
|
@ -20,6 +23,7 @@ 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.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -31,15 +35,28 @@ import com.nutomic.syncthingandroid.R;
|
|||
import com.nutomic.syncthingandroid.fragments.DevicesFragment;
|
||||
import com.nutomic.syncthingandroid.fragments.DrawerFragment;
|
||||
import com.nutomic.syncthingandroid.fragments.FoldersFragment;
|
||||
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Shows {@link com.nutomic.syncthingandroid.fragments.FoldersFragment} and {@link com.nutomic.syncthingandroid.fragments.DevicesFragment} in different tabs, and
|
||||
* Shows {@link com.nutomic.syncthingandroid.fragments.FoldersFragment} and
|
||||
* {@link com.nutomic.syncthingandroid.fragments.DevicesFragment} in different tabs, and
|
||||
* {@link com.nutomic.syncthingandroid.fragments.DrawerFragment} in the navigation drawer.
|
||||
*/
|
||||
public class MainActivity extends SyncthingActivity
|
||||
implements SyncthingService.OnApiChangeListener {
|
||||
|
||||
private static final String TAG = "MainActivity";
|
||||
|
||||
/**
|
||||
* Time after first start when usage reporting dialog should be shown.
|
||||
*
|
||||
* @see #showUsageReportingDialog()
|
||||
*/
|
||||
private static final long USAGE_REPORTING_DIALOG_DELAY = 3 * 24 * 60 * 60 * 1000;
|
||||
|
||||
private AlertDialog mLoadingDialog;
|
||||
|
||||
private AlertDialog mDisabledDialog;
|
||||
|
@ -49,64 +66,88 @@ public class MainActivity extends SyncthingActivity
|
|||
*/
|
||||
@Override
|
||||
@SuppressLint("InflateParams")
|
||||
public void onApiChange(final SyncthingService.State currentState) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (currentState != SyncthingService.State.ACTIVE && !isFinishing()) {
|
||||
if (currentState == SyncthingService.State.DISABLED) {
|
||||
if (mLoadingDialog != null) {
|
||||
mLoadingDialog.dismiss();
|
||||
mLoadingDialog = null;
|
||||
}
|
||||
mDisabledDialog = SyncthingService.showDisabledDialog(MainActivity.this);
|
||||
} else if (mLoadingDialog == null) {
|
||||
final SharedPreferences prefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
|
||||
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
View dialogLayout = inflater.inflate(R.layout.loading_dialog, null);
|
||||
TextView loadingText = (TextView) dialogLayout.findViewById(R.id.loading_text);
|
||||
loadingText.setText((getService().isFirstStart())
|
||||
? R.string.web_gui_creating_key
|
||||
: R.string.api_loading);
|
||||
|
||||
mLoadingDialog = new AlertDialog.Builder(MainActivity.this)
|
||||
.setCancelable(false)
|
||||
.setView(dialogLayout)
|
||||
.show();
|
||||
|
||||
// Make sure the first start dialog is shown on top.
|
||||
if (prefs.getBoolean("first_start", true)) {
|
||||
new AlertDialog.Builder(MainActivity.this)
|
||||
.setTitle(R.string.welcome_title)
|
||||
.setMessage(R.string.welcome_text)
|
||||
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
prefs.edit().putBoolean("first_start", false).commit();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public void onApiChange(SyncthingService.State currentState) {
|
||||
if (currentState == SyncthingService.State.ACTIVE &&
|
||||
new Date().getTime() > getFirstStartTime() + USAGE_REPORTING_DIALOG_DELAY &&
|
||||
getApi().getUsageReportAccepted() == RestApi.UsageReportSetting.UNDECIDED) {
|
||||
showUsageReportingDialog();
|
||||
} else if (currentState != SyncthingService.State.ACTIVE && !isFinishing()) {
|
||||
if (currentState == SyncthingService.State.DISABLED) {
|
||||
if (mLoadingDialog != null) {
|
||||
mLoadingDialog.dismiss();
|
||||
mLoadingDialog = null;
|
||||
}
|
||||
if (mDisabledDialog != null) {
|
||||
mDisabledDialog.dismiss();
|
||||
mDisabledDialog = null;
|
||||
mDisabledDialog = SyncthingService.showDisabledDialog(MainActivity.this);
|
||||
} else if (mLoadingDialog == null) {
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
View dialogLayout = inflater.inflate(R.layout.loading_dialog, null);
|
||||
TextView loadingText = (TextView) dialogLayout.findViewById(R.id.loading_text);
|
||||
loadingText.setText((getService().isFirstStart())
|
||||
? R.string.web_gui_creating_key
|
||||
: R.string.api_loading);
|
||||
|
||||
mLoadingDialog = new AlertDialog.Builder(MainActivity.this)
|
||||
.setCancelable(false)
|
||||
.setView(dialogLayout)
|
||||
.show();
|
||||
|
||||
final SharedPreferences sp =
|
||||
PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
|
||||
|
||||
// Make sure the first start dialog is shown on top.
|
||||
if (sp.getBoolean("first_start", true)) {
|
||||
showFirstStartDialog(sp);
|
||||
}
|
||||
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
|
||||
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (mLoadingDialog != null) {
|
||||
mLoadingDialog.dismiss();
|
||||
mLoadingDialog = null;
|
||||
}
|
||||
if (mDisabledDialog != null) {
|
||||
mDisabledDialog.dismiss();
|
||||
mDisabledDialog = null;
|
||||
}
|
||||
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
|
||||
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unix timestamp at which the app was first installed.
|
||||
*/
|
||||
@TargetApi(9)
|
||||
private long getFirstStartTime() {
|
||||
PackageManager pm = getPackageManager();
|
||||
long firstInstallTime = 0;
|
||||
try {
|
||||
// No info is available on Froyo.
|
||||
firstInstallTime = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD)
|
||||
? pm.getPackageInfo(getPackageName(), 0).firstInstallTime
|
||||
: 0;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, "This should never happen", e);
|
||||
}
|
||||
return firstInstallTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays information for first app start.
|
||||
*/
|
||||
private void showFirstStartDialog(final SharedPreferences sp) {
|
||||
new AlertDialog.Builder(MainActivity.this)
|
||||
.setTitle(R.string.welcome_title)
|
||||
.setMessage(R.string.welcome_text)
|
||||
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
sp.edit().putBoolean("first_start", false).commit();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private final FragmentPagerAdapter mSectionsPagerAdapter =
|
||||
|
@ -227,7 +268,7 @@ public class MainActivity extends SyncthingActivity
|
|||
}
|
||||
|
||||
/**
|
||||
* Saves fragment states.
|
||||
* Saves current tab index and fragment states.
|
||||
*/
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
|
@ -263,7 +304,7 @@ public class MainActivity extends SyncthingActivity
|
|||
|
||||
|
||||
/**
|
||||
* Receives drawer opened and closed events.
|
||||
* Handles drawer opened and closed events, toggling option menu state.
|
||||
*/
|
||||
public class Toggle extends ActionBarDrawerToggle {
|
||||
public Toggle(Activity activity, DrawerLayout drawerLayout) {
|
||||
|
@ -310,4 +351,38 @@ public class MainActivity extends SyncthingActivity
|
|||
return super.onKeyDown(keyCode, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays dialog asking user to accept/deny usage reporting.
|
||||
*/
|
||||
@SuppressLint("InflateParams")
|
||||
private void showUsageReportingDialog() {
|
||||
final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
getApi().setUsageReportAccepted(
|
||||
(which == DialogInterface.BUTTON_POSITIVE)
|
||||
? RestApi.UsageReportSetting.ACCEPTED
|
||||
: RestApi.UsageReportSetting.DENIED,
|
||||
MainActivity.this);
|
||||
}
|
||||
};
|
||||
|
||||
getApi().getUsageReport(new RestApi.OnReceiveUsageReportListener() {
|
||||
@Override
|
||||
public void onReceiveUsageReport(String report) {
|
||||
View v = LayoutInflater.from(MainActivity.this)
|
||||
.inflate(R.layout.usage_reporting_dialog, null);
|
||||
TextView tv = (TextView) v.findViewById(R.id.example);
|
||||
tv.setText(report);
|
||||
new AlertDialog.Builder(MainActivity.this)
|
||||
.setTitle(R.string.usage_reporting_dialog_title)
|
||||
.setView(v)
|
||||
.setPositiveButton(R.string.yes, listener)
|
||||
.setNegativeButton(R.string.no, listener)
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -72,8 +72,8 @@ public class SettingsFragment extends PreferenceFragment
|
|||
value = api.getLocalDevice().name;
|
||||
break;
|
||||
case USAGE_REPORT_ACCEPTED:
|
||||
String v = api.getValue(RestApi.TYPE_OPTIONS, pref.getKey());
|
||||
value = (v.equals("1")) ? "true" : "false";
|
||||
RestApi.UsageReportSetting setting = api.getUsageReportAccepted();
|
||||
value = Boolean.toString(setting == RestApi.UsageReportSetting.ACCEPTED);
|
||||
break;
|
||||
default:
|
||||
value = api.getValue(RestApi.TYPE_OPTIONS, pref.getKey());
|
||||
|
@ -240,8 +240,10 @@ public class SettingsFragment extends PreferenceFragment
|
|||
updated.name = (String) o;
|
||||
mSyncthingService.getApi().editDevice(updated, getActivity(), null);
|
||||
} else if (preference.getKey().equals(USAGE_REPORT_ACCEPTED)) {
|
||||
mSyncthingService.getApi().setValue(RestApi.TYPE_OPTIONS, preference.getKey(),
|
||||
((Boolean) o) ? 1 : 0, false, getActivity());
|
||||
RestApi.UsageReportSetting setting = ((Boolean) o)
|
||||
? RestApi.UsageReportSetting.ACCEPTED
|
||||
: RestApi.UsageReportSetting.DENIED;
|
||||
mSyncthingService.getApi().setUsageReportAccepted(setting, getActivity());
|
||||
} else if (mOptionsScreen.findPreference(preference.getKey()) != null) {
|
||||
boolean isArray = preference.getKey().equals("listenAddress") ||
|
||||
preference.getKey().equals("globalAnnounceServers");
|
||||
|
|
|
@ -29,17 +29,13 @@ public class GetTask extends AsyncTask<String, Void, String> {
|
|||
|
||||
private static final String TAG = "GetTask";
|
||||
|
||||
public static final String URI_CONFIG = "/rest/system/config";
|
||||
|
||||
public static final String URI_VERSION = "/rest/system/version";
|
||||
|
||||
public static final String URI_SYSTEM = "/rest/system/status";
|
||||
|
||||
public static final String URI_CONFIG = "/rest/system/config";
|
||||
public static final String URI_VERSION = "/rest/system/version";
|
||||
public static final String URI_SYSTEM = "/rest/system/status";
|
||||
public static final String URI_CONNECTIONS = "/rest/system/connections";
|
||||
|
||||
public static final String URI_MODEL = "/rest/db/status";
|
||||
|
||||
public static final String URI_DEVICEID = "/rest/svc/deviceid";
|
||||
public static final String URI_MODEL = "/rest/db/status";
|
||||
public static final String URI_DEVICEID = "/rest/svc/deviceid";
|
||||
public static final String URI_REPORT = "/rest/svc/report";
|
||||
|
||||
private String mHttpsCertPath;
|
||||
|
||||
|
|
|
@ -21,8 +21,7 @@ public class PostTask extends AsyncTask<String, Void, Boolean> {
|
|||
private static final String TAG = "PostTask";
|
||||
|
||||
public static final String URI_CONFIG = "/rest/system/config";
|
||||
|
||||
public static final String URI_SCAN = "/rest/db/scan";
|
||||
public static final String URI_SCAN = "/rest/db/scan";
|
||||
|
||||
private String mHttpsCertPath;
|
||||
|
||||
|
|
|
@ -1017,4 +1017,67 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
return mGuiPassword;
|
||||
}
|
||||
|
||||
public enum UsageReportSetting {
|
||||
UNDECIDED,
|
||||
ACCEPTED,
|
||||
DENIED,
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value of usage reporting preference.
|
||||
*/
|
||||
public UsageReportSetting getUsageReportAccepted() {
|
||||
try {
|
||||
switch (mConfig.getJSONObject(TYPE_OPTIONS).getInt("urAccepted")) {
|
||||
case 0: return UsageReportSetting.UNDECIDED;
|
||||
case 1: return UsageReportSetting.ACCEPTED;
|
||||
case -1: return UsageReportSetting.DENIED;
|
||||
default: throw new RuntimeException("Invalid usage report value");
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.w(TAG, "Failed to read usage report value", e);
|
||||
return UsageReportSetting.DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets new value for usage reporting preference.
|
||||
*/
|
||||
public void setUsageReportAccepted(UsageReportSetting value, Activity activity) {
|
||||
int v = 0;
|
||||
switch (value) {
|
||||
case ACCEPTED: v = 1; break;
|
||||
case DENIED: v = -1; break;
|
||||
}
|
||||
try {
|
||||
mConfig.getJSONObject(TYPE_OPTIONS).put("urAccepted", v);
|
||||
} catch (JSONException e) {
|
||||
Log.w(TAG, "Failed to set usage report value", e);
|
||||
}
|
||||
requireRestart(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for {@link #getUsageReport}.
|
||||
*/
|
||||
public interface OnReceiveUsageReportListener {
|
||||
public void onReceiveUsageReport(String report);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns prettyfied usage report.
|
||||
*/
|
||||
public void getUsageReport(final OnReceiveUsageReportListener listener) {
|
||||
new GetTask(mHttpsCertPath) {
|
||||
@Override
|
||||
protected void onPostExecute(String s) {
|
||||
try {
|
||||
listener.onReceiveUsageReport(new JSONObject(s).toString(4));
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException("Failed to prettify usage report", e);
|
||||
}
|
||||
}
|
||||
}.execute(mUrl, GetTask.URI_REPORT, mApiKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -315,6 +315,7 @@ public class SyncthingService extends Service implements
|
|||
new RestApi.OnApiAvailableListener() {
|
||||
@Override
|
||||
public void onApiAvailable() {
|
||||
mCurrentState = State.ACTIVE;
|
||||
onApiChange();
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
|
@ -436,7 +437,7 @@ public class SyncthingService extends Service implements
|
|||
return;
|
||||
}
|
||||
Log.i(TAG, "Web GUI has come online at " + mConfig.getWebGuiUrl());
|
||||
mCurrentState = State.ACTIVE;
|
||||
mCurrentState = State.STARTING;
|
||||
onApiChange();
|
||||
for (OnWebGuiAvailableListener listener : mOnWebGuiAvailableListeners) {
|
||||
listener.onWebGuiAvailable();
|
||||
|
|
23
src/main/res/layout/usage_reporting_dialog.xml
Normal file
23
src/main/res/layout/usage_reporting_dialog.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="10dip">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="10dip"
|
||||
android:text="@string/usage_reporting_dialog_description" />
|
||||
|
||||
<HorizontalScrollView android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/light_grey"
|
||||
android:padding="5dip"
|
||||
android:id="@+id/example"/>
|
||||
</HorizontalScrollView>
|
||||
|
||||
</LinearLayout>
|
|
@ -3,4 +3,5 @@
|
|||
<color name="text_red">#ffff4444</color>
|
||||
<color name="text_blue">#ff33b5e5</color>
|
||||
<color name="text_green">#ff99cc00</color>
|
||||
<color name="light_grey">#cccccc</color>
|
||||
</resources>
|
||||
|
|
|
@ -20,6 +20,14 @@
|
|||
<!-- Text for FoldersFragment and DevicesFragment loading view -->
|
||||
<string name="api_loading">Loading…</string>
|
||||
|
||||
<string name="usage_reporting_dialog_title">Allow Anonymous Usage Reporting?</string>
|
||||
|
||||
<string name="usage_reporting_dialog_description">The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.\n\nThe aggregated statistics are publicly available at https://data.syncthing.net.</string>
|
||||
|
||||
<string name="yes">Yes</string>
|
||||
|
||||
<string name="no">No</string>
|
||||
|
||||
<!-- FoldersFragment -->
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue