diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1e82231..d992a57 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -54,6 +54,7 @@ + @@ -89,7 +90,8 @@ android:authorities="net.typeblog.shelter.documents" android:grantUriPermissions="true" android:permission="android.permission.MANAGE_DOCUMENTS" - android:exported="true"> + android:exported="true" + android:enabled="false"> diff --git a/app/src/main/java/net/typeblog/shelter/ShelterApplication.java b/app/src/main/java/net/typeblog/shelter/ShelterApplication.java index d2fb177..8458680 100644 --- a/app/src/main/java/net/typeblog/shelter/ShelterApplication.java +++ b/app/src/main/java/net/typeblog/shelter/ShelterApplication.java @@ -8,6 +8,7 @@ import android.content.ServiceConnection; import net.typeblog.shelter.services.FileShuttleService; import net.typeblog.shelter.services.ShelterService; import net.typeblog.shelter.util.LocalStorageManager; +import net.typeblog.shelter.util.SettingsManager; public class ShelterApplication extends Application { private ServiceConnection mShelterServiceConnection = null; @@ -17,6 +18,7 @@ public class ShelterApplication extends Application { public void onCreate() { super.onCreate(); LocalStorageManager.initialize(this); + SettingsManager.initialize(this); } public void bindShelterService(ServiceConnection conn, boolean foreground) { diff --git a/app/src/main/java/net/typeblog/shelter/ui/DummyActivity.java b/app/src/main/java/net/typeblog/shelter/ui/DummyActivity.java index 664d76b..d23819c 100644 --- a/app/src/main/java/net/typeblog/shelter/ui/DummyActivity.java +++ b/app/src/main/java/net/typeblog/shelter/ui/DummyActivity.java @@ -26,6 +26,7 @@ import net.typeblog.shelter.services.IFileShuttleService; import net.typeblog.shelter.services.IFileShuttleServiceCallback; import net.typeblog.shelter.util.FileProviderProxy; import net.typeblog.shelter.util.LocalStorageManager; +import net.typeblog.shelter.util.SettingsManager; import net.typeblog.shelter.util.Utility; import java.io.File; @@ -51,6 +52,7 @@ public class DummyActivity extends Activity { // This is a bad experience, so we use two to avoid this. public static final String START_FILE_SHUTTLE = "net.typeblog.shelter.action.START_FILE_SHUTTLE"; public static final String START_FILE_SHUTTLE_2 = "net.typeblog.shelter.action.START_FILE_SHUTTLE_2"; + public static final String SYNCHRONIZE_PREFERENCE = "net.typeblog.shelter.action.SYNCHRONIZE_PREFERENCE"; private static final int REQUEST_INSTALL_PACKAGE = 1; private static final int REQUEST_PERMISSION_EXTERNAL_STORAGE= 2; @@ -69,6 +71,7 @@ public class DummyActivity extends Activity { // so that we can make sure those are updated with our app Utility.enforceWorkProfilePolicies(this); Utility.enforceUserRestrictions(this); + SettingsManager.getInstance().applyAll(); } Intent intent = getIntent(); @@ -93,6 +96,8 @@ public class DummyActivity extends Activity { actionFreezeAllInList(); } else if (START_FILE_SHUTTLE.equals(intent.getAction()) || START_FILE_SHUTTLE_2.equals(intent.getAction())) { actionStartFileShuttle(); + } else if (SYNCHRONIZE_PREFERENCE.equals(intent.getAction())) { + actionSynchronizePreference(); } else { finish(); } @@ -331,4 +336,15 @@ public class DummyActivity extends Activity { } }); } + + private void actionSynchronizePreference() { + String name = getIntent().getStringExtra("name"); + if (getIntent().hasExtra("boolean")) { + LocalStorageManager.getInstance() + .setBoolean(name, getIntent().getBooleanExtra("boolean", false)); + } + // TODO: Cases for other types + SettingsManager.getInstance().applyAll(); + finish(); + } } diff --git a/app/src/main/java/net/typeblog/shelter/ui/MainActivity.java b/app/src/main/java/net/typeblog/shelter/ui/MainActivity.java index 7e11601..0212abb 100644 --- a/app/src/main/java/net/typeblog/shelter/ui/MainActivity.java +++ b/app/src/main/java/net/typeblog/shelter/ui/MainActivity.java @@ -31,6 +31,7 @@ import net.typeblog.shelter.services.IAppInstallCallback; import net.typeblog.shelter.services.IShelterService; import net.typeblog.shelter.services.KillerService; import net.typeblog.shelter.util.LocalStorageManager; +import net.typeblog.shelter.util.SettingsManager; import net.typeblog.shelter.util.UriForwardProxy; import net.typeblog.shelter.util.Utility; @@ -120,6 +121,8 @@ public class MainActivity extends AppCompatActivity { (dialog, which) -> finish()) .show(); } else { + // Initialize the settings + SettingsManager.getInstance().applyAll(); // Initialize the app (start by binding the services) bindServices(); } diff --git a/app/src/main/java/net/typeblog/shelter/ui/SettingsFragment.java b/app/src/main/java/net/typeblog/shelter/ui/SettingsFragment.java index c0c606a..d2c6a23 100644 --- a/app/src/main/java/net/typeblog/shelter/ui/SettingsFragment.java +++ b/app/src/main/java/net/typeblog/shelter/ui/SettingsFragment.java @@ -4,15 +4,22 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; +import android.support.v7.preference.CheckBoxPreference; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceFragmentCompat; import net.typeblog.shelter.R; +import net.typeblog.shelter.util.SettingsManager; -public class SettingsFragment extends PreferenceFragmentCompat { +public class SettingsFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceChangeListener { private static final String SETTINGS_VERSION = "settings_version"; private static final String SETTINGS_SOURCE_CODE = "settings_source_code"; private static final String SETTINGS_BUG_REPORT = "settings_bug_report"; + private static final String SETTINGS_CROSS_PROFILE_FILE_CHOOSER = "settings_cross_profile_file_chooser"; + + private SettingsManager mManager = SettingsManager.getInstance(); + + private CheckBoxPreference mPrefCrossProfileFileChooser = null; @Override public void onCreatePreferences(Bundle bundle, String s) { @@ -32,6 +39,11 @@ public class SettingsFragment extends PreferenceFragmentCompat { .setOnPreferenceClickListener(this::openSummaryUrl); findPreference(SETTINGS_BUG_REPORT) .setOnPreferenceClickListener(this::openSummaryUrl); + + // === Interactions === + mPrefCrossProfileFileChooser = (CheckBoxPreference) findPreference(SETTINGS_CROSS_PROFILE_FILE_CHOOSER); + mPrefCrossProfileFileChooser.setChecked(mManager.getCrossProfileFileChooserEnabled()); + mPrefCrossProfileFileChooser.setOnPreferenceChangeListener(this); } private boolean openSummaryUrl(Preference pref) { @@ -40,4 +52,14 @@ public class SettingsFragment extends PreferenceFragmentCompat { startActivity(intent); return true; } + + @Override + public boolean onPreferenceChange(Preference preference, Object newState) { + if (preference == mPrefCrossProfileFileChooser) { + mManager.setCrossProfileFileChooserEnabled((boolean) newState); + return true; + } else { + return false; + } + } } diff --git a/app/src/main/java/net/typeblog/shelter/util/LocalStorageManager.java b/app/src/main/java/net/typeblog/shelter/util/LocalStorageManager.java index f2b7fc3..bc7ff0e 100644 --- a/app/src/main/java/net/typeblog/shelter/util/LocalStorageManager.java +++ b/app/src/main/java/net/typeblog/shelter/util/LocalStorageManager.java @@ -12,6 +12,7 @@ public class LocalStorageManager { public static final String PREF_IS_SETTING_UP = "is_setting_up"; public static final String PREF_HAS_SETUP = "has_setup"; public static final String PREF_AUTO_FREEZE_LIST_WORK_PROFILE = "auto_freeze_list_work_profile"; + public static final String PREF_CROSS_PROFILE_FILE_CHOOSER = "cross_profile_file_chooser"; private static final String LIST_DIVIDER = ","; diff --git a/app/src/main/java/net/typeblog/shelter/util/SettingsManager.java b/app/src/main/java/net/typeblog/shelter/util/SettingsManager.java new file mode 100644 index 0000000..ec10942 --- /dev/null +++ b/app/src/main/java/net/typeblog/shelter/util/SettingsManager.java @@ -0,0 +1,62 @@ +package net.typeblog.shelter.util; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; + +import net.typeblog.shelter.ui.DummyActivity; + +public class SettingsManager { + private static SettingsManager sInstance = null; + + public static void initialize(Context context) { + sInstance = new SettingsManager(context); + } + + public static SettingsManager getInstance() { + return sInstance; + } + + private LocalStorageManager mStorage = LocalStorageManager.getInstance(); + private Context mContext; + + private SettingsManager(Context context) { + mContext = context; + } + + private void syncSettingsToProfileBool(String name, boolean value) { + Intent intent = new Intent(DummyActivity.SYNCHRONIZE_PREFERENCE); + intent.putExtra("name", name); + intent.putExtra("boolean", value); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Utility.transferIntentToProfile(mContext, intent); + mContext.startActivity(intent); + } + + // Enforce all settings + public void applyAll() { + applyCrossProfileFileChooser(); + } + + // Read and apply the enabled state of the cross profile file chooser + public void applyCrossProfileFileChooser() { + boolean enabled = mStorage.getBoolean(LocalStorageManager.PREF_CROSS_PROFILE_FILE_CHOOSER); + mContext.getPackageManager().setComponentEnabledSetting( + new ComponentName(mContext, CrossProfileDocumentsProvider.class), + enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + } + + // Set the enabled state of the cross profile file chooser + public void setCrossProfileFileChooserEnabled(boolean enabled) { + mStorage.setBoolean(LocalStorageManager.PREF_CROSS_PROFILE_FILE_CHOOSER, enabled); + applyCrossProfileFileChooser(); + syncSettingsToProfileBool(LocalStorageManager.PREF_CROSS_PROFILE_FILE_CHOOSER, enabled); + } + + // Get the enabled state of the cross profile file chooser + public boolean getCrossProfileFileChooserEnabled() { + return mStorage.getBoolean(LocalStorageManager.PREF_CROSS_PROFILE_FILE_CHOOSER); + } +} diff --git a/app/src/main/java/net/typeblog/shelter/util/Utility.java b/app/src/main/java/net/typeblog/shelter/util/Utility.java index 925222c..1f8b956 100644 --- a/app/src/main/java/net/typeblog/shelter/util/Utility.java +++ b/app/src/main/java/net/typeblog/shelter/util/Utility.java @@ -116,6 +116,11 @@ public class Utility { new IntentFilter(DummyActivity.START_FILE_SHUTTLE_2), DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED); + manager.addCrossProfileIntentFilter( + adminComponent, + new IntentFilter(DummyActivity.SYNCHRONIZE_PREFERENCE), + DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT); + // Allow ACTION_SEND and ACTION_SEND_MULTIPLE to cross from managed to parent // TODO: Make this configurable IntentFilter actionSendFilter = new IntentFilter(); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 87a7228..8445bbd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -36,6 +36,9 @@ Settings + Interaction + Cross-profile Documents UI + When enabled, an option will be shown in the system\'s Documents UI (named Files or Downloads on your launcher) and apps that support Documents UI to allow browsing / viewing / picking / copying files in Shelter from main profile and vice-versa. About Version Source Code diff --git a/app/src/main/res/xml/preferences_settings.xml b/app/src/main/res/xml/preferences_settings.xml index d84a3fb..42bca5e 100644 --- a/app/src/main/res/xml/preferences_settings.xml +++ b/app/src/main/res/xml/preferences_settings.xml @@ -2,6 +2,16 @@ + + + + + +