From 561506ff2ebc96c99639c13732a567d28bfa58b9 Mon Sep 17 00:00:00 2001 From: Kai Winter Date: Sat, 6 Oct 2018 12:48:09 +0200 Subject: [PATCH 1/7] Created build variants for google and f-droid The google build variant contains the original sources which seems to needs modifications for f-droid. The f-droid variant contains nothing yet. --- .idea/caches/build_file_checksums.ser | Bin 535 -> 540 bytes app/build.gradle | 11 +++++++++++ .../ca/pkay/rcloneexplorer/MainActivity.java | 0 .../Services/FirebaseIdService.java | 0 .../Services/FirebaseMessagingService.java | 0 .../Settings/NotificationsSettingsFragment.java | 0 .../Settings/SettingsActivity.java | 0 .../Settings/SettingsFragment.java | 0 .../layout/notification_settings_fragment.xml | 0 .../res/layout/settings_fragment.xml | 0 app/src/{main => google}/res/values/strings.xml | 0 11 files changed, 11 insertions(+) rename app/src/{main => google}/java/ca/pkay/rcloneexplorer/MainActivity.java (100%) rename app/src/{main => google}/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java (100%) rename app/src/{main => google}/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java (100%) rename app/src/{main => google}/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java (100%) rename app/src/{main => google}/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java (100%) rename app/src/{main => google}/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java (100%) rename app/src/{main => google}/res/layout/notification_settings_fragment.xml (100%) rename app/src/{main => google}/res/layout/settings_fragment.xml (100%) rename app/src/{main => google}/res/values/strings.xml (100%) diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 8eab31a734056df5bdd0569a0bd6c1201f20acf1..5fce6d185f108166c02be044bdd14708cdd7e642 100644 GIT binary patch delta 159 zcmbQvGKXctbk;Pph|9|+&hfVpNGvFbNh;0ENzqF$N=(T~EiGbTigsG_S^NhZN5}&v z28N0P1_lO!xVUD;M^?#Z;*}RHc0V_im~6?YC#~&l6%$&VT2vepo|#vY>YbPw12rXP z@;*js;V5g5YG>{RHedax?fdY}N~nZ^XYvO|vB`Cex&o7GoSzoYdAj^zy@yWF4t93?|{ diff --git a/app/build.gradle b/app/build.gradle index 7c849a6..8572fbe 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,6 +17,17 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + flavorDimensions "store" + productFlavors { + fdroid { + dimension "store" + versionNameSuffix "-fdroid" + } + google { + dimension "store" + versionNameSuffix "-google" + } + } } repositories { diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/MainActivity.java b/app/src/google/java/ca/pkay/rcloneexplorer/MainActivity.java similarity index 100% rename from app/src/main/java/ca/pkay/rcloneexplorer/MainActivity.java rename to app/src/google/java/ca/pkay/rcloneexplorer/MainActivity.java diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java b/app/src/google/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java similarity index 100% rename from app/src/main/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java rename to app/src/google/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java b/app/src/google/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java similarity index 100% rename from app/src/main/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java rename to app/src/google/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java b/app/src/google/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java similarity index 100% rename from app/src/main/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java rename to app/src/google/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java b/app/src/google/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java similarity index 100% rename from app/src/main/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java rename to app/src/google/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java b/app/src/google/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java similarity index 100% rename from app/src/main/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java rename to app/src/google/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java diff --git a/app/src/main/res/layout/notification_settings_fragment.xml b/app/src/google/res/layout/notification_settings_fragment.xml similarity index 100% rename from app/src/main/res/layout/notification_settings_fragment.xml rename to app/src/google/res/layout/notification_settings_fragment.xml diff --git a/app/src/main/res/layout/settings_fragment.xml b/app/src/google/res/layout/settings_fragment.xml similarity index 100% rename from app/src/main/res/layout/settings_fragment.xml rename to app/src/google/res/layout/settings_fragment.xml diff --git a/app/src/main/res/values/strings.xml b/app/src/google/res/values/strings.xml similarity index 100% rename from app/src/main/res/values/strings.xml rename to app/src/google/res/values/strings.xml From 6fa0dcda65298902c4eefa8c743a944546b40b4b Mon Sep 17 00:00:00 2001 From: Kai Winter Date: Sat, 6 Oct 2018 12:48:51 +0200 Subject: [PATCH 2/7] Modifications for f-droid variant --- app/build.gradle | 15 +- app/src/fdroid/AndroidManifest.xml | 133 ++++ .../ca/pkay/rcloneexplorer/MainActivity.java | 649 ++++++++++++++++++ .../Services/FirebaseIdService.java | 6 + .../Services/FirebaseMessagingService.java | 55 ++ .../NotificationsSettingsFragment.java | 180 +++++ .../Settings/SettingsActivity.java | 166 +++++ .../Settings/SettingsFragment.java | 96 +++ .../layout/notification_settings_fragment.xml | 112 +++ .../fdroid/res/layout/settings_fragment.xml | 167 +++++ app/src/fdroid/res/values/strings.xml | 368 ++++++++++ app/src/google/AndroidManifest.xml | 133 ++++ app/src/main/AndroidManifest.xml | 133 +--- build.gradle | 8 +- 14 files changed, 2080 insertions(+), 141 deletions(-) create mode 100644 app/src/fdroid/AndroidManifest.xml create mode 100644 app/src/fdroid/java/ca/pkay/rcloneexplorer/MainActivity.java create mode 100644 app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java create mode 100644 app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java create mode 100644 app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java create mode 100644 app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java create mode 100644 app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java create mode 100644 app/src/fdroid/res/layout/notification_settings_fragment.xml create mode 100644 app/src/fdroid/res/layout/settings_fragment.xml create mode 100644 app/src/fdroid/res/values/strings.xml create mode 100644 app/src/google/AndroidManifest.xml diff --git a/app/build.gradle b/app/build.gradle index 8572fbe..deba1a5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,7 @@ apply plugin: 'com.android.application' -apply plugin: 'io.fabric' +if (getGradle().getStartParameter().getTaskRequests().toString().toLowerCase().contains("google")) { + apply plugin: 'io.fabric' +} android { compileSdkVersion 27 @@ -51,13 +53,14 @@ dependencies { implementation 'com.github.GrenderG:Toasty:1.3.0' implementation 'com.android.support:support-v4:27.1.1' implementation 'com.github.bumptech.glide:glide:4.7.1' - implementation 'com.google.firebase:firebase-core:16.0.3' - implementation 'com.crashlytics.sdk.android:crashlytics:2.9.5' - implementation 'com.google.firebase:firebase-messaging:17.3.0' + googleImplementation 'com.google.firebase:firebase-core:16.0.3' + googleImplementation 'com.crashlytics.sdk.android:crashlytics:2.9.5' + googleImplementation 'com.google.firebase:firebase-messaging:17.3.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' } - -apply plugin: 'com.google.gms.google-services' +if (getGradle().getStartParameter().getTaskRequests().toString().toLowerCase().contains("google")) { + apply plugin: 'com.google.gms.google-services' +} diff --git a/app/src/fdroid/AndroidManifest.xml b/app/src/fdroid/AndroidManifest.xml new file mode 100644 index 0000000..9c14236 --- /dev/null +++ b/app/src/fdroid/AndroidManifest.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/MainActivity.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/MainActivity.java new file mode 100644 index 0000000..2ea2b27 --- /dev/null +++ b/app/src/fdroid/java/ca/pkay/rcloneexplorer/MainActivity.java @@ -0,0 +1,649 @@ +package ca.pkay.rcloneexplorer; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.ActivityManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AlertDialog; +import android.support.design.widget.NavigationView; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.text.InputType; +import android.util.TypedValue; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SubMenu; +import android.view.View; +import android.widget.Toast; + +import com.crashlytics.android.Crashlytics; +import com.google.firebase.messaging.FirebaseMessaging; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import ca.pkay.rcloneexplorer.Dialogs.InputDialog; +import ca.pkay.rcloneexplorer.Dialogs.LoadingDialog; +import ca.pkay.rcloneexplorer.Fragments.FileExplorerFragment; +import ca.pkay.rcloneexplorer.Fragments.RemotesFragment; +import ca.pkay.rcloneexplorer.Items.RemoteItem; +import ca.pkay.rcloneexplorer.Settings.SettingsActivity; +import es.dmoral.toasty.Toasty; +import io.fabric.sdk.android.Fabric; + +public class MainActivity extends AppCompatActivity + implements NavigationView.OnNavigationItemSelectedListener, + RemotesFragment.OnRemoteClickListener, + RemotesFragment.AddRemoteToNavDrawer, + InputDialog.OnPositive { + + private static final int READ_REQUEST_CODE = 42; // code when opening rclone config file + private static final int REQUEST_PERMISSION_CODE = 62; // code when requesting permissions + private static final int SETTINGS_CODE = 71; // code when coming back from settings + private static final int WRITE_REQUEST_CODE = 81; // code when exporting config + private final String FILE_EXPLORER_FRAGMENT_TAG = "ca.pkay.rcexplorer.MAIN_ACTIVITY_FILE_EXPLORER_TAG"; + private NavigationView navigationView; + private DrawerLayout drawer; + private Rclone rclone; + private Fragment fragment; + private Context context; + private Boolean isDarkTheme; + private HashMap drawerPinnedRemoteIds; + private int availableDrawerPinnedRemoteId; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getIntent() != null) { + String s = getIntent().getStringExtra(getString(R.string.firebase_msg_app_updates_topic)); + if (s != null && s.equals("true")) { + openAppUpdate(); + finish(); + return; + } + + s = getIntent().getStringExtra(getString(R.string.firebase_msg_beta_app_updates_topic)); + if (s != null) { + openBetaUpdate(s); + finish(); + return; + } + } + + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + boolean enableCrashReports = sharedPreferences.getBoolean(getString(R.string.pref_key_crash_reports), false); + if (enableCrashReports) { + Fabric.with(this, new Crashlytics()); + } + + applyTheme(); + context = this; + drawerPinnedRemoteIds = new HashMap<>(); + availableDrawerPinnedRemoteId = 2; + setContentView(R.layout.activity_main); + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + ActionBar actionbar = getSupportActionBar(); + if (actionbar != null) { + actionbar.setDisplayHomeAsUpEnabled(true); + actionbar.setHomeAsUpIndicator(R.drawable.ic_menu); + } + + drawer = findViewById(R.id.drawer_layout); + navigationView = findViewById(R.id.nav_view); + navigationView.setNavigationItemSelectedListener(this); + + requestPermissions(); + + rclone = new Rclone(this); + + findViewById(R.id.locked_config_btn).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + askForConfigPassword(); + } + }); + + boolean appUpdates = sharedPreferences.getBoolean(getString(R.string.pref_key_app_updates), false); + if (appUpdates) { + FirebaseMessaging.getInstance().subscribeToTopic(getString(R.string.firebase_msg_app_updates_topic)); + } + + Intent intent = getIntent(); + Bundle bundle = intent.getExtras(); + + int lastVersionCode = sharedPreferences.getInt(getString(R.string.pref_key_version_code), -1); + String lastVersionName = sharedPreferences.getString(getString(R.string.pref_key_version_name), ""); + int currentVersionCode = BuildConfig.VERSION_CODE; + String currentVersionName = BuildConfig.VERSION_NAME; + + if (!rclone.isRcloneBinaryCreated()) { + new CreateRcloneBinary().execute(); + } else if (lastVersionCode < currentVersionCode || !lastVersionName.equals(currentVersionName)) { + // In version code 24 there were changes to app shortcuts + // Remove this in the long future + if (lastVersionCode <= 23) { + AppShortcutsHelper.removeAllAppShortcuts(this); + AppShortcutsHelper.populateAppShortcuts(this, rclone.getRemotes()); + } + + new CreateRcloneBinary().execute(); + + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putInt(getString(R.string.pref_key_version_code), currentVersionCode); + editor.putString(getString(R.string.pref_key_version_name), currentVersionName); + editor.apply(); + } else if (rclone.isConfigEncrypted()) { + askForConfigPassword(); + } else if (savedInstanceState != null) { + fragment = getSupportFragmentManager().findFragmentByTag(FILE_EXPLORER_FRAGMENT_TAG); + if (fragment instanceof FileExplorerFragment) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.flFragment, fragment, FILE_EXPLORER_FRAGMENT_TAG); + transaction.commit(); + } else { + startRemotesFragment(); + } + } else if (bundle != null && bundle.containsKey(AppShortcutsHelper.APP_SHORTCUT_REMOTE_NAME)) { + String remoteName = bundle.getString(AppShortcutsHelper.APP_SHORTCUT_REMOTE_NAME); + RemoteItem remoteItem = getRemoteItemFromName(remoteName); + if (remoteItem != null) { + AppShortcutsHelper.reportAppShortcutUsage(this, remoteItem.getName()); + startRemote(remoteItem, false); + } else { + Toasty.error(this, getString(R.string.remote_not_found), Toast.LENGTH_SHORT, true).show(); + finish(); + } + } else { + startRemotesFragment(); + } + } + + @Override + protected void onStart() { + super.onStart(); + pinRemotesToDrawer(); + } + + private void applyTheme() { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + int customPrimaryColor = sharedPreferences.getInt(getString(R.string.pref_key_color_primary), -1); + int customAccentColor = sharedPreferences.getInt(getString(R.string.pref_key_color_accent), -1); + isDarkTheme = sharedPreferences.getBoolean(getString(R.string.pref_key_dark_theme), false); + getTheme().applyStyle(CustomColorHelper.getPrimaryColorTheme(this, customPrimaryColor), true); + getTheme().applyStyle(CustomColorHelper.getAccentColorTheme(this, customAccentColor), true); + if (isDarkTheme) { + getTheme().applyStyle(R.style.DarkTheme, true); + } else { + getTheme().applyStyle(R.style.LightTheme, true); + } + + TypedValue typedValue = new TypedValue(); + getTheme().resolveAttribute(R.attr.colorPrimaryDark, typedValue, true); + getWindow().setStatusBarColor(typedValue.data); + + // set recents app color to the primary color + Bitmap bm = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round); + ActivityManager.TaskDescription taskDesc = new ActivityManager.TaskDescription(getString(R.string.app_name), bm, customPrimaryColor); + setTaskDescription(taskDesc); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home && !(fragment instanceof FileExplorerFragment)) { + drawer.openDrawer(GravityCompat.START); + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + public void openNavigationDrawer() { + drawer.openDrawer(GravityCompat.START); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + // result from file picker (for importing config file) + if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) { + Uri uri; + if (data != null) { + uri = data.getData(); + new CopyConfigFile().execute(uri); + } + } else if (requestCode == SETTINGS_CODE && resultCode == RESULT_OK) { + boolean themeChanged = data.getBooleanExtra(SettingsActivity.THEME_CHANGED, false); + if (themeChanged) { + recreate(); + } + } else if (requestCode == WRITE_REQUEST_CODE && resultCode == RESULT_OK) { + Uri uri; + if (data != null) { + uri = data.getData(); + try { + rclone.exportConfigFile(uri); + } catch (IOException e) { + e.printStackTrace(); + Toasty.error(this, getString(R.string.error_exporting_config_file), Toast.LENGTH_SHORT, true).show(); + } + } + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + File dir = getExternalCacheDir(); + if (dir != null && dir.isDirectory()) { + String[] children = dir.list(); + for (String aChildren : children) { + new File(dir, aChildren).delete(); + } + } + } + + @Override + public void onBackPressed() { + DrawerLayout drawer = findViewById(R.id.drawer_layout); + if (drawer.isDrawerOpen(GravityCompat.START)) { + drawer.closeDrawer(GravityCompat.START); + } else if (fragment != null && fragment instanceof FileExplorerFragment) { + if (((FileExplorerFragment) fragment).onBackButtonPressed()) { + return; + } else { + fragment = null; + } + } + super.onBackPressed(); + } + + @Override + public boolean onNavigationItemSelected(@NonNull MenuItem item) { + // Handle navigation view item clicks here. + int id = item.getItemId(); + + if (drawerPinnedRemoteIds.containsKey(id)) { + startPinnedRemote(drawerPinnedRemoteIds.get(id)); + return true; + } + + switch (id) { + case R.id.nav_remotes: + startRemotesFragment(); + break; + case R.id.nav_import: + if (rclone.isConfigFileCreated()) { + warnUserAboutOverwritingConfiguration(); + } else { + importConfigFile(); + } + break; + case R.id.nav_export: + if (rclone.isConfigFileCreated()) { + exportConfigFile(); + } else { + Toasty.info(this, getString(R.string.no_config_found), Toast.LENGTH_SHORT, true).show(); + } + break; + case R.id.nav_settings: + Intent settingsIntent = new Intent(this, SettingsActivity.class); + startActivityForResult(settingsIntent, SETTINGS_CODE); + break; + case R.id.nav_about: + Intent aboutIntent = new Intent(this, AboutActivity.class); + startActivity(aboutIntent); + break; + } + + DrawerLayout drawer = findViewById(R.id.drawer_layout); + drawer.closeDrawer(GravityCompat.START); + return true; + } + + private void pinRemotesToDrawer() { + Menu menu = navigationView.getMenu(); + MenuItem existingMenu = menu.findItem(1); + if (existingMenu != null) { + return; + } + + SubMenu subMenu = menu.addSubMenu(R.id.drawer_pinned_header, 1, Menu.NONE, R.string.nav_drawer_pinned_header); + + List remoteItems = rclone.getRemotes(); + Collections.sort(remoteItems); + for (RemoteItem remoteItem : remoteItems) { + if (remoteItem.isDrawerPinned()) { + MenuItem menuItem = subMenu.add(R.id.nav_pinned, availableDrawerPinnedRemoteId, Menu.NONE, remoteItem.getName()); + drawerPinnedRemoteIds.put(availableDrawerPinnedRemoteId, remoteItem); + availableDrawerPinnedRemoteId++; + menuItem.setIcon(remoteItem.getRemoteIcon()); + } + } + } + + private void startRemotesFragment() { + fragment = RemotesFragment.newInstance(); + FragmentManager fragmentManager = getSupportFragmentManager(); + + for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) { + fragmentManager.popBackStack(); + } + + if (!isFinishing()) { + fragmentManager.beginTransaction().replace(R.id.flFragment, fragment).commitAllowingStateLoss(); + } + } + + private RemoteItem getRemoteItemFromName(String remoteName) { + List remoteItemList = rclone.getRemotes(); + for (RemoteItem remoteItem : remoteItemList) { + if (remoteItem.getName().equals(remoteName)) { + return remoteItem; + } + } + return null; + } + + private void warnUserAboutOverwritingConfiguration() { + AlertDialog.Builder builder; + if (isDarkTheme) { + builder = new AlertDialog.Builder(this, R.style.DarkDialogTheme); + } else { + builder = new AlertDialog.Builder(this); + } + builder.setTitle(R.string.replace_config_file_question); + builder.setMessage(R.string.config_file_lost_statement); + builder.setPositiveButton(R.string.continue_statement, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + dialogInterface.cancel(); + importConfigFile(); + } + }); + builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + dialogInterface.cancel(); + } + }); + builder.show(); + } + + private void askForConfigPassword() { + findViewById(R.id.locked_config).setVisibility(View.VISIBLE); + new InputDialog() + .setTitle(R.string.config_password_protected) + .setMessage(R.string.please_enter_password) + .setNegativeButton(R.string.cancel) + .setPositiveButton(R.string.okay_confirmation) + .setDarkTheme(isDarkTheme) + .setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD) + .show(getSupportFragmentManager(), "input dialog"); + } + + /* + * Input Dialog callback + */ + @Override + public void onPositive(String tag, String input) { + new DecryptConfig().execute(input); + } + + public void importConfigFile() { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + + startActivityForResult(intent, READ_REQUEST_CODE); + } + + public void exportConfigFile() { + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("text/*"); + intent.putExtra(Intent.EXTRA_TITLE, "rclone.conf"); + startActivityForResult(intent, WRITE_REQUEST_CODE); + } + + public void requestPermissions() { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION_CODE); + } + } + + private void openAppUpdate() { + Uri uri = Uri.parse(getString(R.string.app_latest_release_url)); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivity(intent); + } + + private void openBetaUpdate(String url) { + Uri uri = Uri.parse(url); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivity(intent); + } + + @Override + public void onRemoteClick(RemoteItem remote) { + startRemote(remote, true); + } + + private void startRemote(RemoteItem remote, boolean addToBackStack) { + fragment = FileExplorerFragment.newInstance(remote); + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.flFragment, fragment, FILE_EXPLORER_FRAGMENT_TAG); + if (addToBackStack) { + transaction.addToBackStack(null); + } + transaction.commit(); + + AppShortcutsHelper.reportAppShortcutUsage(this, remote.getName()); + navigationView.getMenu().getItem(0).setChecked(false); + } + + private void startPinnedRemote(RemoteItem remoteItem) { + if (fragment != null && fragment instanceof FileExplorerFragment) { + FragmentManager fragmentManager = getSupportFragmentManager(); + + // this is the case when remote gets started from a shortcut + // therefore back should exit the app, and not go into remotes screen + if (fragmentManager.getBackStackEntryCount() == 0) { + startRemote(remoteItem, false); + } else { + for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) { + fragmentManager.popBackStack(); + } + + startRemote(remoteItem, true); + } + } else { + startRemote(remoteItem, true); + } + + DrawerLayout drawer = findViewById(R.id.drawer_layout); + drawer.closeDrawer(GravityCompat.START); + } + + @Override + public void addRemoteToNavDrawer() { + Menu menu = navigationView.getMenu(); + + // remove all items and add them again so that it's in alpha order + menu.removeItem(1); + drawerPinnedRemoteIds.clear(); + availableDrawerPinnedRemoteId = 1; + + pinRemotesToDrawer(); + } + + @Override + public void removeRemoteFromNavDrawer() { + Menu menu = navigationView.getMenu(); + + // remove all items and add them again so that it's in alpha order + menu.removeItem(1); + drawerPinnedRemoteIds.clear(); + availableDrawerPinnedRemoteId = 1; + + pinRemotesToDrawer(); + } + + @SuppressLint("StaticFieldLeak") + private class CreateRcloneBinary extends AsyncTask { + + private LoadingDialog loadingDialog; + + @Override + protected void onPreExecute() { + super.onPreExecute(); + loadingDialog = new LoadingDialog() + .setTitle(R.string.creating_rclone_binary) + .setCanCancel(false); + loadingDialog.show(getSupportFragmentManager(), "loading dialog"); + } + + @Override + protected Boolean doInBackground(Void... voids) { + try { + rclone.createRcloneBinary(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + return true; + } + + @Override + protected void onPostExecute(Boolean success) { + super.onPostExecute(success); + if (!success) { + Toasty.error(context, getString(R.string.error_creating_rclone_binary), Toast.LENGTH_LONG, true).show(); + finish(); + System.exit(0); + } + if (loadingDialog.isStateSaved()) { + loadingDialog.dismissAllowingStateLoss(); + } else { + loadingDialog.dismiss(); + } + startRemotesFragment(); + } + } + + @SuppressLint("StaticFieldLeak") + private class CopyConfigFile extends AsyncTask { + + private LoadingDialog loadingDialog; + + @Override + protected void onPreExecute() { + super.onPreExecute(); + findViewById(R.id.locked_config).setVisibility(View.GONE); + loadingDialog = new LoadingDialog() + .setTitle(R.string.copying_rclone_config) + .setCanCancel(false); + loadingDialog.show(getSupportFragmentManager(), "loading dialog"); + } + + @Override + protected Boolean doInBackground(Uri... uris) { + try { + rclone.copyConfigFile(uris[0]); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + return true; + } + + @Override + protected void onPostExecute(Boolean success) { + super.onPostExecute(success); + if (loadingDialog.isStateSaved()) { + loadingDialog.dismissAllowingStateLoss(); + } else { + loadingDialog.dismiss(); + } + if (!success) { + return; + } + + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.remove(getString(R.string.shared_preferences_pinned_remotes)); + editor.remove(getString(R.string.shared_preferences_drawer_pinned_remotes)); + editor.remove(getString(R.string.shared_preferences_hidden_remotes)); + editor.apply(); + + if (rclone.isConfigEncrypted()) { + pinRemotesToDrawer(); // this will clear any previous pinned remotes + askForConfigPassword(); + } else { + AppShortcutsHelper.removeAllAppShortcuts(context); + AppShortcutsHelper.populateAppShortcuts(context, rclone.getRemotes()); + pinRemotesToDrawer(); + startRemotesFragment(); + } + } + } + + @SuppressLint("StaticFieldLeak") + private class DecryptConfig extends AsyncTask { + + private LoadingDialog loadingDialog; + + @Override + protected void onPreExecute() { + super.onPreExecute(); + loadingDialog = new LoadingDialog() + .setTitle(R.string.working) + .setCanCancel(false); + loadingDialog.show(getSupportFragmentManager(), "loading dialog"); + } + + @Override + protected Boolean doInBackground(String... strings) { + return rclone.decryptConfig(strings[0]); + } + + @Override + protected void onPostExecute(Boolean success) { + super.onPostExecute(success); + loadingDialog.dismiss(); + if (!success) { + Toasty.error(context, getString(R.string.error_unlocking_config), Toast.LENGTH_LONG, true).show(); + askForConfigPassword(); + } else { + findViewById(R.id.locked_config).setVisibility(View.GONE); + AppShortcutsHelper.removeAllAppShortcuts(context); + AppShortcutsHelper.populateAppShortcuts(context, rclone.getRemotes()); + startRemotesFragment(); + } + } + } +} diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java new file mode 100644 index 0000000..be81be7 --- /dev/null +++ b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java @@ -0,0 +1,6 @@ +package ca.pkay.rcloneexplorer.Services; + +import com.google.firebase.iid.FirebaseInstanceIdService; + +public class FirebaseIdService extends FirebaseInstanceIdService { +} diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java new file mode 100644 index 0000000..58eb144 --- /dev/null +++ b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java @@ -0,0 +1,55 @@ +package ca.pkay.rcloneexplorer.Services; + +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationManagerCompat; + +import com.google.firebase.messaging.RemoteMessage; + +import ca.pkay.rcloneexplorer.R; + +public class FirebaseMessagingService extends com.google.firebase.messaging.FirebaseMessagingService { + + private final String CHANNEL_ID = "ca.pkay.rcexplorer.app_updates"; + private final String CHANNEL_NAME = "App updates"; + + @Override + public void onMessageReceived(RemoteMessage remoteMessage) { + super.onMessageReceived(remoteMessage); + setNotificationChannel(); + + Uri uri = Uri.parse(getString(R.string.app_latest_release_url)); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0); + + NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(getString(R.string.app_update_notification_title)) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setContentIntent(pendingIntent) + .setAutoCancel(true); + + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); + notificationManager.notify(33, builder.build()); + } + + private void setNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // Create the NotificationChannel, but only on API 26+ because + // the NotificationChannel class is new and not in the support library + NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); + channel.setDescription("App updates notification"); + // Register the channel with the system + NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null) { + notificationManager.createNotificationChannel(channel); + } + } + } +} diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java new file mode 100644 index 0000000..09ef9d8 --- /dev/null +++ b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java @@ -0,0 +1,180 @@ +package ca.pkay.rcloneexplorer.Settings; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.Switch; +import android.widget.Toast; + +import com.google.firebase.messaging.FirebaseMessaging; + +import ca.pkay.rcloneexplorer.R; +import es.dmoral.toasty.Toasty; + +public class NotificationsSettingsFragment extends Fragment { + + private Context context; + private View notificationsElement; + private View appUpdatesElement; + private Switch appUpdatesSwitch; + private View betaAppUpdatesElement; + private Switch betaAppUpdatesSwitch; + + /** + * Mandatory empty constructor for the fragment manager to instantiate the + * fragment (e.g. upon screen orientation changes). + */ + public NotificationsSettingsFragment() { + } + + public static NotificationsSettingsFragment newInstance() { + return new NotificationsSettingsFragment(); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.notification_settings_fragment, container, false); + getViews(view); + setDefaultStates(); + setClickListeners(); + + if (getActivity() != null) { + getActivity().setTitle(getString(R.string.notifications_pref_title)); + } + + return view; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + this.context = context; + } + + private void getViews(View view) { + notificationsElement = view.findViewById(R.id.notifications); + appUpdatesElement = view.findViewById(R.id.app_updates); + appUpdatesSwitch = view.findViewById(R.id.app_updates_switch); + betaAppUpdatesElement = view.findViewById(R.id.beta_app_updates); + betaAppUpdatesSwitch = view.findViewById(R.id.beta_app_updates_switch); + } + + private void setDefaultStates() { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + boolean appUpdates = sharedPreferences.getBoolean(getString(R.string.pref_key_app_updates), false); + boolean betaUpdates = sharedPreferences.getBoolean(getString(R.string.pref_key_app_updates_beta), false); + + appUpdatesSwitch.setChecked(appUpdates); + betaAppUpdatesSwitch.setChecked(betaUpdates); + + if (appUpdates) { + betaAppUpdatesElement.setVisibility(View.VISIBLE); + } else { + betaAppUpdatesElement.setVisibility(View.GONE); + } + } + + private void setClickListeners() { + + notificationsElement.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onNotificationsClicked(); + } + }); + appUpdatesElement.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (appUpdatesSwitch.isChecked()) { + appUpdatesSwitch.setChecked(false); + } else { + appUpdatesSwitch.setChecked(true); + } + } + }); + appUpdatesSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + onAppUpdatesClicked(isChecked); + } + }); + betaAppUpdatesElement.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (betaAppUpdatesSwitch.isChecked()) { + betaAppUpdatesSwitch.setChecked(false); + } else { + betaAppUpdatesSwitch.setChecked(true); + } + } + }); + betaAppUpdatesSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + onBetaAppUpdatesClicked(isChecked); + } + }); + } + + private void onNotificationsClicked() { + Intent intent = new Intent(); + intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); + + //for Android 5-7 + intent.putExtra("app_package", context.getPackageName()); + intent.putExtra("app_uid", context.getApplicationInfo().uid); + + // for Android O + intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName()); + + if (intent.resolveActivity(context.getPackageManager()) != null) { + startActivity(intent); + } else { + Toasty.error(context, "Couldn't find activity to start", Toast.LENGTH_SHORT, true).show(); + } + } + + private void onAppUpdatesClicked(boolean isChecked) { + if (isChecked) { + FirebaseMessaging.getInstance().subscribeToTopic(getString(R.string.firebase_msg_app_updates_topic)); + betaAppUpdatesElement.setVisibility(View.VISIBLE); + } else { + FirebaseMessaging.getInstance().unsubscribeFromTopic(getString(R.string.firebase_msg_app_updates_topic)); + betaAppUpdatesSwitch.setChecked(false); + betaAppUpdatesElement.setVisibility(View.GONE); + } + + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(getString(R.string.pref_key_app_updates), isChecked); + editor.apply(); + } + + private void onBetaAppUpdatesClicked(boolean isChecked) { + if (isChecked) { + FirebaseMessaging.getInstance().subscribeToTopic(getString(R.string.firebase_msg_beta_app_updates_topic)); + } else { + FirebaseMessaging.getInstance().unsubscribeFromTopic(getString(R.string.firebase_msg_beta_app_updates_topic)); + } + + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(getString(R.string.pref_key_app_updates_beta), isChecked); + editor.apply(); + } +} diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java new file mode 100644 index 0000000..650c21d --- /dev/null +++ b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java @@ -0,0 +1,166 @@ +package ca.pkay.rcloneexplorer.Settings; + +import android.app.ActivityManager; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; + +import ca.pkay.rcloneexplorer.CustomColorHelper; +import ca.pkay.rcloneexplorer.R; + +public class SettingsActivity extends AppCompatActivity implements SettingsFragment.OnSettingCategorySelectedListener, + LookAndFeelSettingsFragment.OnThemeHasChanged { + + public final static String THEME_CHANGED = "ca.pkay.rcexplorer.SettingsActivity.THEME_CHANGED"; + private final String SAVED_THEME_CHANGE = "ca.pkay.rcexplorer.SettingsActivity.OUTSTATE_THEME_CHANGED"; + private final String SAVED_FRAGMENT = "ca.pkay.rcexplorer.SettingsActivity.RESTORE_FRAGMENT"; + private boolean themeHasChanged; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + applyTheme(); + setContentView(R.layout.activity_settings); + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayShowHomeEnabled(true); + } + + FragmentManager fragmentManager = getSupportFragmentManager(); + for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) { + fragmentManager.popBackStack(); + } + + startSettingsFragment(); + + if (savedInstanceState != null) { + Fragment fragment = getSupportFragmentManager().findFragmentByTag(SAVED_FRAGMENT); + if (fragment != null) { + restoreFragment(fragment); + } + } + + themeHasChanged = savedInstanceState != null && savedInstanceState.getBoolean(SAVED_THEME_CHANGE, false); + Intent returnData = new Intent(); + returnData.putExtra(THEME_CHANGED, themeHasChanged); + setResult(RESULT_OK, returnData); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(SAVED_THEME_CHANGE, themeHasChanged); + } + + @Override + protected void onStart() { + super.onStart(); + } + + private void applyTheme() { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + int customPrimaryColor = sharedPreferences.getInt(getString(R.string.pref_key_color_primary), -1); + int customAccentColor = sharedPreferences.getInt(getString(R.string.pref_key_color_accent), -1); + boolean isDarkTheme = sharedPreferences.getBoolean(getString(R.string.pref_key_dark_theme), false); + getTheme().applyStyle(CustomColorHelper.getPrimaryColorTheme(this, customPrimaryColor), true); + getTheme().applyStyle(CustomColorHelper.getAccentColorTheme(this, customAccentColor), true); + if (isDarkTheme) { + getTheme().applyStyle(R.style.DarkTheme, true); + } else { + getTheme().applyStyle(R.style.LightTheme, true); + } + + // set recents app color to the primary color + Bitmap bm = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round); + ActivityManager.TaskDescription taskDesc = new ActivityManager.TaskDescription(getString(R.string.app_name), bm, customPrimaryColor); + setTaskDescription(taskDesc); + } + + @Override + public boolean onSupportNavigateUp() { + onBackPressed(); + return true; + } + + private void restoreFragment(Fragment fragment) { + if (fragment instanceof GeneralSettingsFragment) { + startGeneralSettingsFragment(); + } else if (fragment instanceof LookAndFeelSettingsFragment) { + startLookAndFeelSettingsFragment(); + } else if (fragment instanceof NotificationsSettingsFragment) { + startNotificationSettingsFragment(); + } else if (fragment instanceof LoggingSettingsFragment) { + startLoggingSettingsActivity(); + } + } + + private void startSettingsFragment() { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.flFragment, SettingsFragment.newInstance()); + transaction.commit(); + } + + private void startGeneralSettingsFragment() { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.flFragment, GeneralSettingsFragment.newInstance(), SAVED_FRAGMENT); + transaction.addToBackStack(null); + transaction.commit(); + } + + private void startLookAndFeelSettingsFragment() { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.flFragment, LookAndFeelSettingsFragment.newInstance(), SAVED_FRAGMENT); + transaction.addToBackStack(null); + transaction.commit(); + } + + private void startNotificationSettingsFragment() { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.flFragment, NotificationsSettingsFragment.newInstance(), SAVED_FRAGMENT); + transaction.addToBackStack(null); + transaction.commit(); + } + + private void startLoggingSettingsActivity() { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.flFragment, LoggingSettingsFragment.newInstance(), SAVED_FRAGMENT); + transaction.addToBackStack(null); + transaction.commit(); + } + + @Override + public void onSettingCategoryClicked(int category) { + switch (category) { + case SettingsFragment.GENERAL_SETTINGS: + startGeneralSettingsFragment(); + break; + case SettingsFragment.LOOK_AND_FEEL_SETTINGS: + startLookAndFeelSettingsFragment(); + break; + case SettingsFragment.LOGGING_SETTINGS: + startLoggingSettingsActivity(); + break; + case SettingsFragment.NOTIFICATION_SETTINGS: + startNotificationSettingsFragment(); + break; + } + } + + @Override + public void onThemeChanged() { + themeHasChanged = true; + recreate(); + } +} diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java new file mode 100644 index 0000000..a3543aa --- /dev/null +++ b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java @@ -0,0 +1,96 @@ +package ca.pkay.rcloneexplorer.Settings; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import ca.pkay.rcloneexplorer.R; + +public class SettingsFragment extends Fragment { + + public final static int GENERAL_SETTINGS = 1; + public final static int LOOK_AND_FEEL_SETTINGS = 2; + public final static int LOGGING_SETTINGS = 3; + public final static int NOTIFICATION_SETTINGS = 4; + private OnSettingCategorySelectedListener clickListener; + + public interface OnSettingCategorySelectedListener { + void onSettingCategoryClicked(int category); + } + + /** + * Mandatory empty constructor for the fragment manager to instantiate the + * fragment (e.g. upon screen orientation changes). + */ + public SettingsFragment() { + } + + public static SettingsFragment newInstance() { + return new SettingsFragment(); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.settings_fragment, container, false); + setClickListeners(view); + + if (getActivity() != null) { + getActivity().setTitle(getString(R.string.settings)); + } + + return view; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof OnSettingCategorySelectedListener) { + clickListener = (OnSettingCategorySelectedListener) context; + } else { + throw new RuntimeException(context.toString() + " must implement listener"); + } + } + + private void setClickListeners(View view) { + + view.findViewById(R.id.general_settings).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + clickListener.onSettingCategoryClicked(GENERAL_SETTINGS); + } + }); + + view.findViewById(R.id.logging_settings).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + clickListener.onSettingCategoryClicked(LOGGING_SETTINGS); + } + }); + + view.findViewById(R.id.look_and_feel_settings).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + clickListener.onSettingCategoryClicked(LOOK_AND_FEEL_SETTINGS); + } + }); + + view.findViewById(R.id.notification_settings).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + clickListener.onSettingCategoryClicked(NOTIFICATION_SETTINGS); + } + }); + } +} diff --git a/app/src/fdroid/res/layout/notification_settings_fragment.xml b/app/src/fdroid/res/layout/notification_settings_fragment.xml new file mode 100644 index 0000000..d8d3faf --- /dev/null +++ b/app/src/fdroid/res/layout/notification_settings_fragment.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/fdroid/res/layout/settings_fragment.xml b/app/src/fdroid/res/layout/settings_fragment.xml new file mode 100644 index 0000000..a94e8a7 --- /dev/null +++ b/app/src/fdroid/res/layout/settings_fragment.xml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/fdroid/res/values/strings.xml b/app/src/fdroid/res/values/strings.xml new file mode 100644 index 0000000..b2661bb --- /dev/null +++ b/app/src/fdroid/res/values/strings.xml @@ -0,0 +1,368 @@ + + Rclone Explorer + Rclone Ex + MainActivity + + Open navigation drawer + Close navigation drawer + + Settings + About + Changelog + Remotes + Settings + Import rclone config + Export rclone config + About + Sort + Select all + Serve + Name + Date + Size + Download + Move + Rename + Delete + App Version + Rclone Version + Changelog + Credits/Libraries + Star on GitHub + Report a bug + Author + Patryk Kaczmarkiewicz + Follow on GitHub + The folder is empty + Ooops! + No configuration found, please import or add remote + Import configuration + Empty state icon + File icon + • + Remote Icon + New Folder + Cancel + Select + App Icon + No internet connection + https://github.com/kaczmarkiewiczp/rcloneExplorer + https://github.com/kaczmarkiewiczp/rcloneExplorer/issues/new + https://github.com/kaczmarkiewiczp + Replace current configuration file? + Your current configuration will be lost + Continue + Download complete + File downloads + Download + Streaming Service + Serving on http://127.0.0.1:8080 + Streaming service + Upload + Upload complete + File uploads + Remotes + Upload Files + New Folder + Okay + selected + Rename a file + Please type new file name + Select destination + Create new folder + Please type new folder name + Loading file + Please wait + OK + File Renamed + File(s) moved + File(s) deleted + New folder created + Error retrieving directory content + Error retrieving remotes + Error deleting a file + Error creating a directory + Error moving a file + By name + By date modified + By size + Ascending + Descending + Filename + Modified time + Size + MD5 + SHA1 + Tap to calculate + Calculating + ERROR + UNSUPPORTED + Hash copied to clipboard + Copying rclone configuration + Creating rclone binary + Error creating rclone binary + Configuration file is password protected + Please enter your password + Working + Error unlocking configuration + Locked configuration password + Configuration is password protected + Press to unlock + out of + downloads finished + downloads failed + Download failed + files uploaded + files failed to upload + Upload failed + Properties + Open as + Text + Audio + Video + Image + Settings + General + Primary Color + Accent Color + Look and Feel + App restart is required + Select a primary color + Select an accent color + AboutLibsActivity + App icon made by + Smashicons + from + Flaticon + pref_key_stream_max + pref_key_logs + pref_key_color_primary + pref_key_color_accent + pref_key_dark_theme + pref_key_app_updates + pref_key_app_updates_beta + pref_key_crash_reports + pref_key_show_thumbnails + pref_key_wifi_only_transfers + pref_key_version_code + pref_key_version_name + Look and Feel + Primary Color + Accent Color + Dark theme + Transfer files only over Wi-Fi + Uploading and downloading of files will be cancelled when not on Wi-Fi + Miscellaneous + Use logs + Errors will be logged to a local file + Color picker checkmark + Select primary color + Select accent color + Notifications + Connectivity + Open system settings + circle + Loading + Rclone Explorer is not configured + Please open the app to set it up first + Error retrieving files + ca.pkay.rcexplorer.BACKGROUND_SERVICE_BROADCAST + ca.pkay.rcexplorer.BACKGROUND_SERVICE_BROADCAST_DATA_REMOTE + ca.pkay.rcexplorer.BACKGROUND_SERVICE_BROADCAST_DATA_PATH + ca.pkay.rcexplorer.BACKGROUND_SERVICE_BROADCAST_DATA_PATH2 + Background service for possibly long running tasks like Move and Delete + Background service for sync + Moving… + Deleting… + Operation failed + Moving Service + Delete Service + Syncing %1$s + and %1$d other + Search + Search clear button + No search results + Nothing selected + Device storage + Remote name + Amazon Client ID + Amazon Client Secret + Box App Client ID + Box App Client Secret + DropBox App Client ID + DropBox App Client Secret + Google App Client ID + Google App Client Secret + NEXT + Remote name can\'t be empty + Waiting for code + Open browser + CANCEL + NEXT + Box + DropBox + Amazon Cloud Drive + Required field + Auth server URL + Token server URL + Backblaze B2 + Account ID + Application key + Endpoint for the service + Root folder ID - fill in to access Computers folders + FTP host to connect to + FTP username + FTP port + FTP password + FTP + HTTP + URL of http host to connect to + Hubic Client ID + Hubic Client Secret + Hubic + Pcloud Client ID + Pcloud Client Secret + Pcloud + SSH Host to connect to + SSH username + SSH port + SSH password + SFTP/SSH + Yandex + Yandex Client ID + Yandex Client Secret + User name + Password + Webdav + Error creating remote + 21 + 22 + Remote created successfully + Next + Leave blank normally + Configure + Microsoft OneDrive + Microsoft Client ID + Microsoft Client Secret + OneDrive account type + Personal account + Business account + Click to select remote or path to alias + Click to select remote or path to encrypt + Click to select scope + Remote can\'t be empty + Alias + Select a remote + Select path to Alias + Select path to Encrypt + Name of the Webdav service + How to encrypt the filenames + Directory name encryption + Password for encryption + Password for salt + Optional but recommended + QingStor Access Key ID + QingStor Secret Access Key (password) + Enter an enpoint URL to connection QingStor API + Number of connection retries + Crypt + QingStor + Leave blank for anonymous access + Leave blank for default value qingstor.com:443 + Choose a zone to connect to + Leave blank to use the default value (3) + Storage Account Name + Storage Account Key + Microsoft Azure Blob Storage + Local + Drive + There are no remotes + Choose directory + Error exporting config file + app_updates + beta_app_updates + New version of Rclone Explorer is available on GitHub + Get notified about app updates + Get notified about beta releases + Delete remote? + Empty trash + Trash emptied + Error emptying trash + File options + Link + Error generating a link + Generating public link + Public link + Link copied to clipboard + Cache + Click to set chunk size (default 5 MB) + Click to set cache expiry time (default 6 h) + Click to select cache size (default 10 GB) + Chunk size + Chunk size: %1$s + Cache expiry time + Cache expiry time: %1$s + Cache size + Cache size: %1$s + Send anonymous crash reports + Pin to the top + Unpin + Pin to drawer + Unpin from drawer + shared_preferences_pinned_remotes + shared_preferences_drawer_pinned_remotes + https://github.com/kaczmarkiewiczp/rcloneExplorer/releases/latest + Experimental Features + Show thumbnails + Generate Password + Set Password + Generate + Length + Lower case + Upper case + Numbers + Special characters + Refresh button + Show passwords + Wrap filenames + Error retrieving remote %1$s + Go to + Go to + Set default for all SFTP/SSH remotes + home + root + pref_key_go_to_default_set + pref_key_start_at_root + Google Drive Scope + Storage + Select Storage + Are you sure you want to empty the trash? + Application Shortcuts + General + Application Shortcuts + shared_preferences_app_shortcuts + Maximum of 4 shortcuts allowed + Remote not found + Add to home screen + Pick a protocol + Sync + Select sync direction + Pinned + Options + Download cancelled + Wi-Fi connection isn\'t available + Sync operation cancelled + Upload cancelled + Logging + Look and Feel settings icon + General settings icon + Notification settings icon + Logging settings icon + Hidden remotes + shared_preferences_hidden_remotes + Select remotes to hide + Allow remote access + Authentication + Optional + Serve a remote + diff --git a/app/src/google/AndroidManifest.xml b/app/src/google/AndroidManifest.xml new file mode 100644 index 0000000..9c14236 --- /dev/null +++ b/app/src/google/AndroidManifest.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9c14236..c1031d9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,133 +1,2 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/build.gradle b/build.gradle index ede6ee2..0373d4e 100644 --- a/build.gradle +++ b/build.gradle @@ -10,9 +10,11 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.1.4' - classpath 'com.google.gms:google-services:4.0.1' // google-services plugin - classpath 'io.fabric.tools:gradle:1.25.4' - + if (getGradle().getStartParameter().getTaskRequests().toString().toLowerCase().contains("google")) { + classpath 'com.google.gms:google-services:4.0.1' // google-services plugin + classpath 'io.fabric.tools:gradle:1.25.4' + } + // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From a19312aa63424377f1ade97c0766005cc0affe02 Mon Sep 17 00:00:00 2001 From: Kai Winter Date: Sat, 6 Oct 2018 12:48:59 +0200 Subject: [PATCH 3/7] Removed Firebase-related code from f-droid variant --- .../ca/pkay/rcloneexplorer/MainActivity.java | 41 -- .../Services/FirebaseIdService.java | 6 - .../Services/FirebaseMessagingService.java | 55 --- .../Settings/LoggingSettingsFragment.java | 100 +++++ .../NotificationsSettingsFragment.java | 91 ----- .../res/layout/logging_settings_fragment.xml | 65 ++++ .../layout/notification_settings_fragment.xml | 66 ---- .../Settings/LoggingSettingsFragment.java | 0 .../res/layout/logging_settings_fragment.xml | 0 app/src/google/res/values/strings.xml | 362 +----------------- .../{fdroid => main}/res/values/strings.xml | 8 - 11 files changed, 167 insertions(+), 627 deletions(-) delete mode 100644 app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java delete mode 100644 app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java create mode 100644 app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java create mode 100644 app/src/fdroid/res/layout/logging_settings_fragment.xml rename app/src/{main => google}/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java (100%) rename app/src/{main => google}/res/layout/logging_settings_fragment.xml (100%) rename app/src/{fdroid => main}/res/values/strings.xml (97%) diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/MainActivity.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/MainActivity.java index 2ea2b27..bb4ae87 100644 --- a/app/src/fdroid/java/ca/pkay/rcloneexplorer/MainActivity.java +++ b/app/src/fdroid/java/ca/pkay/rcloneexplorer/MainActivity.java @@ -36,9 +36,6 @@ import android.view.View; import android.widget.Toast; -import com.crashlytics.android.Crashlytics; -import com.google.firebase.messaging.FirebaseMessaging; - import java.io.File; import java.io.IOException; import java.util.Collections; @@ -52,7 +49,6 @@ import ca.pkay.rcloneexplorer.Items.RemoteItem; import ca.pkay.rcloneexplorer.Settings.SettingsActivity; import es.dmoral.toasty.Toasty; -import io.fabric.sdk.android.Fabric; public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, @@ -77,27 +73,7 @@ public class MainActivity extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (getIntent() != null) { - String s = getIntent().getStringExtra(getString(R.string.firebase_msg_app_updates_topic)); - if (s != null && s.equals("true")) { - openAppUpdate(); - finish(); - return; - } - - s = getIntent().getStringExtra(getString(R.string.firebase_msg_beta_app_updates_topic)); - if (s != null) { - openBetaUpdate(s); - finish(); - return; - } - } - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - boolean enableCrashReports = sharedPreferences.getBoolean(getString(R.string.pref_key_crash_reports), false); - if (enableCrashReports) { - Fabric.with(this, new Crashlytics()); - } applyTheme(); context = this; @@ -127,11 +103,6 @@ public void onClick(View v) { } }); - boolean appUpdates = sharedPreferences.getBoolean(getString(R.string.pref_key_app_updates), false); - if (appUpdates) { - FirebaseMessaging.getInstance().subscribeToTopic(getString(R.string.firebase_msg_app_updates_topic)); - } - Intent intent = getIntent(); Bundle bundle = intent.getExtras(); @@ -436,18 +407,6 @@ public void requestPermissions() { } } - private void openAppUpdate() { - Uri uri = Uri.parse(getString(R.string.app_latest_release_url)); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - startActivity(intent); - } - - private void openBetaUpdate(String url) { - Uri uri = Uri.parse(url); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - startActivity(intent); - } - @Override public void onRemoteClick(RemoteItem remote) { startRemote(remote, true); diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java deleted file mode 100644 index be81be7..0000000 --- a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseIdService.java +++ /dev/null @@ -1,6 +0,0 @@ -package ca.pkay.rcloneexplorer.Services; - -import com.google.firebase.iid.FirebaseInstanceIdService; - -public class FirebaseIdService extends FirebaseInstanceIdService { -} diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java deleted file mode 100644 index 58eb144..0000000 --- a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Services/FirebaseMessagingService.java +++ /dev/null @@ -1,55 +0,0 @@ -package ca.pkay.rcloneexplorer.Services; - -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.support.v4.app.NotificationCompat; -import android.support.v4.app.NotificationManagerCompat; - -import com.google.firebase.messaging.RemoteMessage; - -import ca.pkay.rcloneexplorer.R; - -public class FirebaseMessagingService extends com.google.firebase.messaging.FirebaseMessagingService { - - private final String CHANNEL_ID = "ca.pkay.rcexplorer.app_updates"; - private final String CHANNEL_NAME = "App updates"; - - @Override - public void onMessageReceived(RemoteMessage remoteMessage) { - super.onMessageReceived(remoteMessage); - setNotificationChannel(); - - Uri uri = Uri.parse(getString(R.string.app_latest_release_url)); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0); - - NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID) - .setSmallIcon(R.drawable.ic_notification) - .setContentTitle(getString(R.string.app_update_notification_title)) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setContentIntent(pendingIntent) - .setAutoCancel(true); - - NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); - notificationManager.notify(33, builder.build()); - } - - private void setNotificationChannel() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - // Create the NotificationChannel, but only on API 26+ because - // the NotificationChannel class is new and not in the support library - NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); - channel.setDescription("App updates notification"); - // Register the channel with the system - NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager != null) { - notificationManager.createNotificationChannel(channel); - } - } - } -} diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java new file mode 100644 index 0000000..add967e --- /dev/null +++ b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java @@ -0,0 +1,100 @@ +package ca.pkay.rcloneexplorer.Settings; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.Switch; +import android.widget.Toast; + +import ca.pkay.rcloneexplorer.R; +import es.dmoral.toasty.Toasty; + +public class LoggingSettingsFragment extends Fragment { + + private Context context; + private Switch useLogsSwitch; + private View useLogsElement; + + /** + * Mandatory empty constructor for the fragment manager to instantiate the + * fragment (e.g. upon screen orientation changes). + */ + public LoggingSettingsFragment() { + } + + public static LoggingSettingsFragment newInstance() { + return new LoggingSettingsFragment(); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.logging_settings_fragment, container, false); + getViews(view); + setDefaultStates(); + setClickListeners(); + + if (getActivity() != null) { + getActivity().setTitle(getString(R.string.logging_settings_header)); + } + + return view; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + this.context = context; + } + + private void getViews(View view) { + useLogsSwitch = view.findViewById(R.id.use_logs_switch); + useLogsElement = view.findViewById(R.id.use_logs); + } + + private void setDefaultStates() { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + boolean useLogs = sharedPreferences.getBoolean(getString(R.string.pref_key_logs), false); + + useLogsSwitch.setChecked(useLogs); + } + + private void setClickListeners() { + useLogsElement.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (useLogsSwitch.isChecked()) { + useLogsSwitch.setChecked(false); + } else { + useLogsSwitch.setChecked(true); + } + } + }); + useLogsSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + onUseLogsClicked(isChecked); + } + }); + } + + private void onUseLogsClicked(boolean isChecked) { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(getString(R.string.pref_key_logs), isChecked); + editor.apply(); + } +} diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java index 09ef9d8..3d17b40 100644 --- a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java +++ b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java @@ -2,21 +2,15 @@ import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.CompoundButton; -import android.widget.Switch; import android.widget.Toast; -import com.google.firebase.messaging.FirebaseMessaging; - import ca.pkay.rcloneexplorer.R; import es.dmoral.toasty.Toasty; @@ -24,10 +18,6 @@ public class NotificationsSettingsFragment extends Fragment { private Context context; private View notificationsElement; - private View appUpdatesElement; - private Switch appUpdatesSwitch; - private View betaAppUpdatesElement; - private Switch betaAppUpdatesSwitch; /** * Mandatory empty constructor for the fragment manager to instantiate the @@ -50,7 +40,6 @@ public void onCreate(@Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.notification_settings_fragment, container, false); getViews(view); - setDefaultStates(); setClickListeners(); if (getActivity() != null) { @@ -68,25 +57,6 @@ public void onAttach(Context context) { private void getViews(View view) { notificationsElement = view.findViewById(R.id.notifications); - appUpdatesElement = view.findViewById(R.id.app_updates); - appUpdatesSwitch = view.findViewById(R.id.app_updates_switch); - betaAppUpdatesElement = view.findViewById(R.id.beta_app_updates); - betaAppUpdatesSwitch = view.findViewById(R.id.beta_app_updates_switch); - } - - private void setDefaultStates() { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - boolean appUpdates = sharedPreferences.getBoolean(getString(R.string.pref_key_app_updates), false); - boolean betaUpdates = sharedPreferences.getBoolean(getString(R.string.pref_key_app_updates_beta), false); - - appUpdatesSwitch.setChecked(appUpdates); - betaAppUpdatesSwitch.setChecked(betaUpdates); - - if (appUpdates) { - betaAppUpdatesElement.setVisibility(View.VISIBLE); - } else { - betaAppUpdatesElement.setVisibility(View.GONE); - } } private void setClickListeners() { @@ -97,38 +67,6 @@ public void onClick(View v) { onNotificationsClicked(); } }); - appUpdatesElement.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (appUpdatesSwitch.isChecked()) { - appUpdatesSwitch.setChecked(false); - } else { - appUpdatesSwitch.setChecked(true); - } - } - }); - appUpdatesSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - onAppUpdatesClicked(isChecked); - } - }); - betaAppUpdatesElement.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (betaAppUpdatesSwitch.isChecked()) { - betaAppUpdatesSwitch.setChecked(false); - } else { - betaAppUpdatesSwitch.setChecked(true); - } - } - }); - betaAppUpdatesSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - onBetaAppUpdatesClicked(isChecked); - } - }); } private void onNotificationsClicked() { @@ -148,33 +86,4 @@ private void onNotificationsClicked() { Toasty.error(context, "Couldn't find activity to start", Toast.LENGTH_SHORT, true).show(); } } - - private void onAppUpdatesClicked(boolean isChecked) { - if (isChecked) { - FirebaseMessaging.getInstance().subscribeToTopic(getString(R.string.firebase_msg_app_updates_topic)); - betaAppUpdatesElement.setVisibility(View.VISIBLE); - } else { - FirebaseMessaging.getInstance().unsubscribeFromTopic(getString(R.string.firebase_msg_app_updates_topic)); - betaAppUpdatesSwitch.setChecked(false); - betaAppUpdatesElement.setVisibility(View.GONE); - } - - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putBoolean(getString(R.string.pref_key_app_updates), isChecked); - editor.apply(); - } - - private void onBetaAppUpdatesClicked(boolean isChecked) { - if (isChecked) { - FirebaseMessaging.getInstance().subscribeToTopic(getString(R.string.firebase_msg_beta_app_updates_topic)); - } else { - FirebaseMessaging.getInstance().unsubscribeFromTopic(getString(R.string.firebase_msg_beta_app_updates_topic)); - } - - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putBoolean(getString(R.string.pref_key_app_updates_beta), isChecked); - editor.apply(); - } } diff --git a/app/src/fdroid/res/layout/logging_settings_fragment.xml b/app/src/fdroid/res/layout/logging_settings_fragment.xml new file mode 100644 index 0000000..d3bea20 --- /dev/null +++ b/app/src/fdroid/res/layout/logging_settings_fragment.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/fdroid/res/layout/notification_settings_fragment.xml b/app/src/fdroid/res/layout/notification_settings_fragment.xml index d8d3faf..13fed91 100644 --- a/app/src/fdroid/res/layout/notification_settings_fragment.xml +++ b/app/src/fdroid/res/layout/notification_settings_fragment.xml @@ -37,72 +37,6 @@ android:text="@string/open_system_settings"/> - - - - - - - - - - - - - - - - - - - - - Rclone Explorer - Rclone Ex - MainActivity - - Open navigation drawer - Close navigation drawer - - Settings - About - Changelog - Remotes - Settings - Import rclone config - Export rclone config - About - Sort - Select all - Serve - Name - Date - Size - Download - Move - Rename - Delete - App Version - Rclone Version - Changelog - Credits/Libraries - Star on GitHub - Report a bug - Author - Patryk Kaczmarkiewicz - Follow on GitHub - The folder is empty - Ooops! - No configuration found, please import or add remote - Import configuration - Empty state icon - File icon - • - Remote Icon - New Folder - Cancel - Select - App Icon - No internet connection - https://github.com/kaczmarkiewiczp/rcloneExplorer - https://github.com/kaczmarkiewiczp/rcloneExplorer/issues/new - https://github.com/kaczmarkiewiczp - Replace current configuration file? - Your current configuration will be lost - Continue - Download complete - File downloads - Download - Streaming Service - Serving on http://127.0.0.1:8080 - Streaming service - Upload - Upload complete - File uploads - Remotes - Upload Files - New Folder - Okay - selected - Rename a file - Please type new file name - Select destination - Create new folder - Please type new folder name - Loading file - Please wait - OK - File Renamed - File(s) moved - File(s) deleted - New folder created - Error retrieving directory content - Error retrieving remotes - Error deleting a file - Error creating a directory - Error moving a file - By name - By date modified - By size - Ascending - Descending - Filename - Modified time - Size - MD5 - SHA1 - Tap to calculate - Calculating - ERROR - UNSUPPORTED - Hash copied to clipboard - Copying rclone configuration - Creating rclone binary - Error creating rclone binary - Configuration file is password protected - Please enter your password - Working - Error unlocking configuration - Locked configuration password - Configuration is password protected - Press to unlock - out of - downloads finished - downloads failed - Download failed - files uploaded - files failed to upload - Upload failed - Properties - Open as - Text - Audio - Video - Image - Settings - General - Primary Color - Accent Color - Look and Feel - App restart is required - Select a primary color - Select an accent color - AboutLibsActivity - App icon made by - Smashicons - from - Flaticon - pref_key_stream_max - pref_key_logs - pref_key_color_primary - pref_key_color_accent - pref_key_dark_theme + pref_key_crash_reports pref_key_app_updates pref_key_app_updates_beta - pref_key_crash_reports - pref_key_show_thumbnails - pref_key_wifi_only_transfers - pref_key_version_code - pref_key_version_name - Look and Feel - Primary Color - Accent Color - Dark theme - Transfer files only over Wi-Fi - Uploading and downloading of files will be cancelled when not on Wi-Fi - Miscellaneous - Use logs - Errors will be logged to a local file - Color picker checkmark - Select primary color - Select accent color - Notifications - Connectivity - Open system settings - circle - Loading - Rclone Explorer is not configured - Please open the app to set it up first - Error retrieving files - ca.pkay.rcexplorer.BACKGROUND_SERVICE_BROADCAST - ca.pkay.rcexplorer.BACKGROUND_SERVICE_BROADCAST_DATA_REMOTE - ca.pkay.rcexplorer.BACKGROUND_SERVICE_BROADCAST_DATA_PATH - ca.pkay.rcexplorer.BACKGROUND_SERVICE_BROADCAST_DATA_PATH2 - Background service for possibly long running tasks like Move and Delete - Background service for sync - Moving… - Deleting… - Operation failed - Moving Service - Delete Service - Syncing %1$s - and %1$d other - Search - Search clear button - No search results - Nothing selected - Device storage - Remote name - Amazon Client ID - Amazon Client Secret - Box App Client ID - Box App Client Secret - DropBox App Client ID - DropBox App Client Secret - Google App Client ID - Google App Client Secret - NEXT - Remote name can\'t be empty - Waiting for code - Open browser - CANCEL - NEXT - Box - DropBox - Amazon Cloud Drive - Required field - Auth server URL - Token server URL - Backblaze B2 - Account ID - Application key - Endpoint for the service - Root folder ID - fill in to access Computers folders - FTP host to connect to - FTP username - FTP port - FTP password - FTP - HTTP - URL of http host to connect to - Hubic Client ID - Hubic Client Secret - Hubic - Pcloud Client ID - Pcloud Client Secret - Pcloud - SSH Host to connect to - SSH username - SSH port - SSH password - SFTP/SSH - Yandex - Yandex Client ID - Yandex Client Secret - User name - Password - Webdav - Error creating remote - 21 - 22 - Remote created successfully - Next - Leave blank normally - Configure - Microsoft OneDrive - Microsoft Client ID - Microsoft Client Secret - OneDrive account type - Personal account - Business account - Click to select remote or path to alias - Click to select remote or path to encrypt - Click to select scope - Remote can\'t be empty - Alias - Select a remote - Select path to Alias - Select path to Encrypt - Name of the Webdav service - How to encrypt the filenames - Directory name encryption - Password for encryption - Password for salt - Optional but recommended - QingStor Access Key ID - QingStor Secret Access Key (password) - Enter an enpoint URL to connection QingStor API - Number of connection retries - Crypt - QingStor - Leave blank for anonymous access - Leave blank for default value qingstor.com:443 - Choose a zone to connect to - Leave blank to use the default value (3) - Storage Account Name - Storage Account Key - Microsoft Azure Blob Storage - Local - Drive - There are no remotes - Choose directory - Error exporting config file - app_updates beta_app_updates - New version of Rclone Explorer is available on GitHub + app_updates Get notified about app updates Get notified about beta releases - Delete remote? - Empty trash - Trash emptied - Error emptying trash - File options - Link - Error generating a link - Generating public link - Public link - Link copied to clipboard - Cache - Click to set chunk size (default 5 MB) - Click to set cache expiry time (default 6 h) - Click to select cache size (default 10 GB) - Chunk size - Chunk size: %1$s - Cache expiry time - Cache expiry time: %1$s - Cache size - Cache size: %1$s Send anonymous crash reports - Pin to the top - Unpin - Pin to drawer - Unpin from drawer - shared_preferences_pinned_remotes - shared_preferences_drawer_pinned_remotes - https://github.com/kaczmarkiewiczp/rcloneExplorer/releases/latest - Experimental Features - Show thumbnails - Generate Password - Set Password - Generate - Length - Lower case - Upper case - Numbers - Special characters - Refresh button - Show passwords - Wrap filenames - Error retrieving remote %1$s - Go to - Go to - Set default for all SFTP/SSH remotes - home - root - pref_key_go_to_default_set - pref_key_start_at_root - Google Drive Scope - Storage - Select Storage - Are you sure you want to empty the trash? - Application Shortcuts - General - Application Shortcuts - shared_preferences_app_shortcuts - Maximum of 4 shortcuts allowed - Remote not found - Add to home screen - Pick a protocol - Sync - Select sync direction - Pinned - Options - Download cancelled - Wi-Fi connection isn\'t available - Sync operation cancelled - Upload cancelled - Logging - Look and Feel settings icon - General settings icon - Notification settings icon - Logging settings icon - Hidden remotes - shared_preferences_hidden_remotes - Select remotes to hide - Allow remote access - Authentication - Optional - Serve a remote diff --git a/app/src/fdroid/res/values/strings.xml b/app/src/main/res/values/strings.xml similarity index 97% rename from app/src/fdroid/res/values/strings.xml rename to app/src/main/res/values/strings.xml index b2661bb..9d50951 100644 --- a/app/src/fdroid/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -139,9 +139,6 @@ pref_key_color_primary pref_key_color_accent pref_key_dark_theme - pref_key_app_updates - pref_key_app_updates_beta - pref_key_crash_reports pref_key_show_thumbnails pref_key_wifi_only_transfers pref_key_version_code @@ -279,11 +276,7 @@ There are no remotes Choose directory Error exporting config file - app_updates - beta_app_updates New version of Rclone Explorer is available on GitHub - Get notified about app updates - Get notified about beta releases Delete remote? Empty trash Trash emptied @@ -304,7 +297,6 @@ Cache expiry time: %1$s Cache size Cache size: %1$s - Send anonymous crash reports Pin to the top Unpin Pin to drawer From f493a510f0b6cee7572325844bb096b69601a220 Mon Sep 17 00:00:00 2001 From: Kai Winter Date: Sat, 6 Oct 2018 12:49:07 +0200 Subject: [PATCH 4/7] Moved SettingsFragment and ~Activity to main --- .../Settings/SettingsActivity.java | 166 ----------------- .../Settings/SettingsFragment.java | 96 ---------- .../google/res/layout/settings_fragment.xml | 167 ------------------ .../Settings/SettingsActivity.java | 0 .../Settings/SettingsFragment.java | 0 .../res/layout/settings_fragment.xml | 0 6 files changed, 429 deletions(-) delete mode 100644 app/src/google/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java delete mode 100644 app/src/google/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java delete mode 100644 app/src/google/res/layout/settings_fragment.xml rename app/src/{fdroid => main}/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java (100%) rename app/src/{fdroid => main}/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java (100%) rename app/src/{fdroid => main}/res/layout/settings_fragment.xml (100%) diff --git a/app/src/google/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java b/app/src/google/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java deleted file mode 100644 index 650c21d..0000000 --- a/app/src/google/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java +++ /dev/null @@ -1,166 +0,0 @@ -package ca.pkay.rcloneexplorer.Settings; - -import android.app.ActivityManager; -import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; - -import ca.pkay.rcloneexplorer.CustomColorHelper; -import ca.pkay.rcloneexplorer.R; - -public class SettingsActivity extends AppCompatActivity implements SettingsFragment.OnSettingCategorySelectedListener, - LookAndFeelSettingsFragment.OnThemeHasChanged { - - public final static String THEME_CHANGED = "ca.pkay.rcexplorer.SettingsActivity.THEME_CHANGED"; - private final String SAVED_THEME_CHANGE = "ca.pkay.rcexplorer.SettingsActivity.OUTSTATE_THEME_CHANGED"; - private final String SAVED_FRAGMENT = "ca.pkay.rcexplorer.SettingsActivity.RESTORE_FRAGMENT"; - private boolean themeHasChanged; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - applyTheme(); - setContentView(R.layout.activity_settings); - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setDisplayShowHomeEnabled(true); - } - - FragmentManager fragmentManager = getSupportFragmentManager(); - for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) { - fragmentManager.popBackStack(); - } - - startSettingsFragment(); - - if (savedInstanceState != null) { - Fragment fragment = getSupportFragmentManager().findFragmentByTag(SAVED_FRAGMENT); - if (fragment != null) { - restoreFragment(fragment); - } - } - - themeHasChanged = savedInstanceState != null && savedInstanceState.getBoolean(SAVED_THEME_CHANGE, false); - Intent returnData = new Intent(); - returnData.putExtra(THEME_CHANGED, themeHasChanged); - setResult(RESULT_OK, returnData); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putBoolean(SAVED_THEME_CHANGE, themeHasChanged); - } - - @Override - protected void onStart() { - super.onStart(); - } - - private void applyTheme() { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - int customPrimaryColor = sharedPreferences.getInt(getString(R.string.pref_key_color_primary), -1); - int customAccentColor = sharedPreferences.getInt(getString(R.string.pref_key_color_accent), -1); - boolean isDarkTheme = sharedPreferences.getBoolean(getString(R.string.pref_key_dark_theme), false); - getTheme().applyStyle(CustomColorHelper.getPrimaryColorTheme(this, customPrimaryColor), true); - getTheme().applyStyle(CustomColorHelper.getAccentColorTheme(this, customAccentColor), true); - if (isDarkTheme) { - getTheme().applyStyle(R.style.DarkTheme, true); - } else { - getTheme().applyStyle(R.style.LightTheme, true); - } - - // set recents app color to the primary color - Bitmap bm = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round); - ActivityManager.TaskDescription taskDesc = new ActivityManager.TaskDescription(getString(R.string.app_name), bm, customPrimaryColor); - setTaskDescription(taskDesc); - } - - @Override - public boolean onSupportNavigateUp() { - onBackPressed(); - return true; - } - - private void restoreFragment(Fragment fragment) { - if (fragment instanceof GeneralSettingsFragment) { - startGeneralSettingsFragment(); - } else if (fragment instanceof LookAndFeelSettingsFragment) { - startLookAndFeelSettingsFragment(); - } else if (fragment instanceof NotificationsSettingsFragment) { - startNotificationSettingsFragment(); - } else if (fragment instanceof LoggingSettingsFragment) { - startLoggingSettingsActivity(); - } - } - - private void startSettingsFragment() { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.flFragment, SettingsFragment.newInstance()); - transaction.commit(); - } - - private void startGeneralSettingsFragment() { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.flFragment, GeneralSettingsFragment.newInstance(), SAVED_FRAGMENT); - transaction.addToBackStack(null); - transaction.commit(); - } - - private void startLookAndFeelSettingsFragment() { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.flFragment, LookAndFeelSettingsFragment.newInstance(), SAVED_FRAGMENT); - transaction.addToBackStack(null); - transaction.commit(); - } - - private void startNotificationSettingsFragment() { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.flFragment, NotificationsSettingsFragment.newInstance(), SAVED_FRAGMENT); - transaction.addToBackStack(null); - transaction.commit(); - } - - private void startLoggingSettingsActivity() { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.flFragment, LoggingSettingsFragment.newInstance(), SAVED_FRAGMENT); - transaction.addToBackStack(null); - transaction.commit(); - } - - @Override - public void onSettingCategoryClicked(int category) { - switch (category) { - case SettingsFragment.GENERAL_SETTINGS: - startGeneralSettingsFragment(); - break; - case SettingsFragment.LOOK_AND_FEEL_SETTINGS: - startLookAndFeelSettingsFragment(); - break; - case SettingsFragment.LOGGING_SETTINGS: - startLoggingSettingsActivity(); - break; - case SettingsFragment.NOTIFICATION_SETTINGS: - startNotificationSettingsFragment(); - break; - } - } - - @Override - public void onThemeChanged() { - themeHasChanged = true; - recreate(); - } -} diff --git a/app/src/google/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java b/app/src/google/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java deleted file mode 100644 index a3543aa..0000000 --- a/app/src/google/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java +++ /dev/null @@ -1,96 +0,0 @@ -package ca.pkay.rcloneexplorer.Settings; - -import android.content.Context; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v7.app.AppCompatActivity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import ca.pkay.rcloneexplorer.R; - -public class SettingsFragment extends Fragment { - - public final static int GENERAL_SETTINGS = 1; - public final static int LOOK_AND_FEEL_SETTINGS = 2; - public final static int LOGGING_SETTINGS = 3; - public final static int NOTIFICATION_SETTINGS = 4; - private OnSettingCategorySelectedListener clickListener; - - public interface OnSettingCategorySelectedListener { - void onSettingCategoryClicked(int category); - } - - /** - * Mandatory empty constructor for the fragment manager to instantiate the - * fragment (e.g. upon screen orientation changes). - */ - public SettingsFragment() { - } - - public static SettingsFragment newInstance() { - return new SettingsFragment(); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.settings_fragment, container, false); - setClickListeners(view); - - if (getActivity() != null) { - getActivity().setTitle(getString(R.string.settings)); - } - - return view; - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (context instanceof OnSettingCategorySelectedListener) { - clickListener = (OnSettingCategorySelectedListener) context; - } else { - throw new RuntimeException(context.toString() + " must implement listener"); - } - } - - private void setClickListeners(View view) { - - view.findViewById(R.id.general_settings).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - clickListener.onSettingCategoryClicked(GENERAL_SETTINGS); - } - }); - - view.findViewById(R.id.logging_settings).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - clickListener.onSettingCategoryClicked(LOGGING_SETTINGS); - } - }); - - view.findViewById(R.id.look_and_feel_settings).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - clickListener.onSettingCategoryClicked(LOOK_AND_FEEL_SETTINGS); - } - }); - - view.findViewById(R.id.notification_settings).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - clickListener.onSettingCategoryClicked(NOTIFICATION_SETTINGS); - } - }); - } -} diff --git a/app/src/google/res/layout/settings_fragment.xml b/app/src/google/res/layout/settings_fragment.xml deleted file mode 100644 index a94e8a7..0000000 --- a/app/src/google/res/layout/settings_fragment.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java b/app/src/main/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java similarity index 100% rename from app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java rename to app/src/main/java/ca/pkay/rcloneexplorer/Settings/SettingsActivity.java diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java b/app/src/main/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java similarity index 100% rename from app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java rename to app/src/main/java/ca/pkay/rcloneexplorer/Settings/SettingsFragment.java diff --git a/app/src/fdroid/res/layout/settings_fragment.xml b/app/src/main/res/layout/settings_fragment.xml similarity index 100% rename from app/src/fdroid/res/layout/settings_fragment.xml rename to app/src/main/res/layout/settings_fragment.xml From d3c03c45d582688791124c41e68650c1ddcb1b9a Mon Sep 17 00:00:00 2001 From: Kai Winter Date: Sat, 6 Oct 2018 13:02:22 +0200 Subject: [PATCH 5/7] Rectified Manifest for variants --- .idea/modules.xml | 2 +- app/src/fdroid/AndroidManifest.xml | 133 ----------------------------- app/src/google/AndroidManifest.xml | 111 +----------------------- app/src/main/AndroidManifest.xml | 117 ++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 245 deletions(-) delete mode 100644 app/src/fdroid/AndroidManifest.xml diff --git a/.idea/modules.xml b/.idea/modules.xml index 3d8ecea..a05354e 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,8 +2,8 @@ - + \ No newline at end of file diff --git a/app/src/fdroid/AndroidManifest.xml b/app/src/fdroid/AndroidManifest.xml deleted file mode 100644 index 9c14236..0000000 --- a/app/src/fdroid/AndroidManifest.xml +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/google/AndroidManifest.xml b/app/src/google/AndroidManifest.xml index 9c14236..23acde4 100644 --- a/app/src/google/AndroidManifest.xml +++ b/app/src/google/AndroidManifest.xml @@ -2,33 +2,7 @@ - - - - - - - - - - - - - - - - - + @@ -39,95 +13,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c1031d9..ac1c725 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,2 +1,117 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 61853db074f05ce6488ea28fddaf0e2a44ec80ea Mon Sep 17 00:00:00 2001 From: Kai Winter Date: Sat, 6 Oct 2018 21:22:36 +0200 Subject: [PATCH 6/7] Moved common things to base class to reduce code duplicity --- .../Settings/LoggingSettingsFragment.java | 98 +------------------ .../NotificationsSettingsFragment.java | 87 +--------------- .../Settings/LoggingSettingsFragment.java | 61 +----------- .../NotificationsSettingsFragment.java | 65 +----------- .../Settings/BaseLoggingSettingsFragment.java | 98 +++++++++++++++++++ .../BaseNotificationsSettingsFragment.java | 88 +++++++++++++++++ 6 files changed, 193 insertions(+), 304 deletions(-) create mode 100644 app/src/main/java/ca/pkay/rcloneexplorer/Settings/BaseLoggingSettingsFragment.java create mode 100644 app/src/main/java/ca/pkay/rcloneexplorer/Settings/BaseNotificationsSettingsFragment.java diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java index add967e..32f8fbf 100644 --- a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java +++ b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java @@ -1,100 +1,4 @@ package ca.pkay.rcloneexplorer.Settings; -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CompoundButton; -import android.widget.Switch; -import android.widget.Toast; - -import ca.pkay.rcloneexplorer.R; -import es.dmoral.toasty.Toasty; - -public class LoggingSettingsFragment extends Fragment { - - private Context context; - private Switch useLogsSwitch; - private View useLogsElement; - - /** - * Mandatory empty constructor for the fragment manager to instantiate the - * fragment (e.g. upon screen orientation changes). - */ - public LoggingSettingsFragment() { - } - - public static LoggingSettingsFragment newInstance() { - return new LoggingSettingsFragment(); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.logging_settings_fragment, container, false); - getViews(view); - setDefaultStates(); - setClickListeners(); - - if (getActivity() != null) { - getActivity().setTitle(getString(R.string.logging_settings_header)); - } - - return view; - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - this.context = context; - } - - private void getViews(View view) { - useLogsSwitch = view.findViewById(R.id.use_logs_switch); - useLogsElement = view.findViewById(R.id.use_logs); - } - - private void setDefaultStates() { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - boolean useLogs = sharedPreferences.getBoolean(getString(R.string.pref_key_logs), false); - - useLogsSwitch.setChecked(useLogs); - } - - private void setClickListeners() { - useLogsElement.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (useLogsSwitch.isChecked()) { - useLogsSwitch.setChecked(false); - } else { - useLogsSwitch.setChecked(true); - } - } - }); - useLogsSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - onUseLogsClicked(isChecked); - } - }); - } - - private void onUseLogsClicked(boolean isChecked) { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putBoolean(getString(R.string.pref_key_logs), isChecked); - editor.apply(); - } +public class LoggingSettingsFragment extends BaseLoggingSettingsFragment { } diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java index 3d17b40..b2b9818 100644 --- a/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java +++ b/app/src/fdroid/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java @@ -1,89 +1,4 @@ package ca.pkay.rcloneexplorer.Settings; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Toast; - -import ca.pkay.rcloneexplorer.R; -import es.dmoral.toasty.Toasty; - -public class NotificationsSettingsFragment extends Fragment { - - private Context context; - private View notificationsElement; - - /** - * Mandatory empty constructor for the fragment manager to instantiate the - * fragment (e.g. upon screen orientation changes). - */ - public NotificationsSettingsFragment() { - } - - public static NotificationsSettingsFragment newInstance() { - return new NotificationsSettingsFragment(); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.notification_settings_fragment, container, false); - getViews(view); - setClickListeners(); - - if (getActivity() != null) { - getActivity().setTitle(getString(R.string.notifications_pref_title)); - } - - return view; - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - this.context = context; - } - - private void getViews(View view) { - notificationsElement = view.findViewById(R.id.notifications); - } - - private void setClickListeners() { - - notificationsElement.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onNotificationsClicked(); - } - }); - } - - private void onNotificationsClicked() { - Intent intent = new Intent(); - intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); - - //for Android 5-7 - intent.putExtra("app_package", context.getPackageName()); - intent.putExtra("app_uid", context.getApplicationInfo().uid); - - // for Android O - intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName()); - - if (intent.resolveActivity(context.getPackageManager()) != null) { - startActivity(intent); - } else { - Toasty.error(context, "Couldn't find activity to start", Toast.LENGTH_SHORT, true).show(); - } - } +public class NotificationsSettingsFragment extends BaseNotificationsSettingsFragment { } diff --git a/app/src/google/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java b/app/src/google/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java index 7fa8560..dfcf0cd 100644 --- a/app/src/google/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java +++ b/app/src/google/java/ca/pkay/rcloneexplorer/Settings/LoggingSettingsFragment.java @@ -1,12 +1,10 @@ package ca.pkay.rcloneexplorer.Settings; -import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -17,54 +15,23 @@ import ca.pkay.rcloneexplorer.R; import es.dmoral.toasty.Toasty; -public class LoggingSettingsFragment extends Fragment { +public class LoggingSettingsFragment extends BaseLoggingSettingsFragment { - private Context context; - private Switch useLogsSwitch; - private View useLogsElement; private View crashReportsElement; private Switch crashReportsSwitch; - /** - * Mandatory empty constructor for the fragment manager to instantiate the - * fragment (e.g. upon screen orientation changes). - */ - public LoggingSettingsFragment() { - } - - public static LoggingSettingsFragment newInstance() { - return new LoggingSettingsFragment(); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.logging_settings_fragment, container, false); + View view = super.onCreateView(inflater, container, savedInstanceState); getViews(view); setDefaultStates(); setClickListeners(); - if (getActivity() != null) { - getActivity().setTitle(getString(R.string.logging_settings_header)); - } - return view; } - @Override - public void onAttach(Context context) { - super.onAttach(context); - this.context = context; - } - private void getViews(View view) { - useLogsSwitch = view.findViewById(R.id.use_logs_switch); - useLogsElement = view.findViewById(R.id.use_logs); crashReportsElement = view.findViewById(R.id.crash_reporting); crashReportsSwitch = view.findViewById(R.id.crash_reporting_switch); } @@ -74,27 +41,10 @@ private void setDefaultStates() { boolean useLogs = sharedPreferences.getBoolean(getString(R.string.pref_key_logs), false); boolean crashReports = sharedPreferences.getBoolean(getString(R.string.pref_key_crash_reports), false); - useLogsSwitch.setChecked(useLogs); crashReportsSwitch.setChecked(crashReports); } private void setClickListeners() { - useLogsElement.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (useLogsSwitch.isChecked()) { - useLogsSwitch.setChecked(false); - } else { - useLogsSwitch.setChecked(true); - } - } - }); - useLogsSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - onUseLogsClicked(isChecked); - } - }); crashReportsElement.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -113,13 +63,6 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { }); } - private void onUseLogsClicked(boolean isChecked) { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putBoolean(getString(R.string.pref_key_logs), isChecked); - editor.apply(); - } - private void crashReportsClicked(boolean isChecked) { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sharedPreferences.edit(); diff --git a/app/src/google/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java b/app/src/google/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java index 09ef9d8..66409f9 100644 --- a/app/src/google/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java +++ b/app/src/google/java/ca/pkay/rcloneexplorer/Settings/NotificationsSettingsFragment.java @@ -1,73 +1,39 @@ package ca.pkay.rcloneexplorer.Settings; -import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.Switch; -import android.widget.Toast; import com.google.firebase.messaging.FirebaseMessaging; import ca.pkay.rcloneexplorer.R; -import es.dmoral.toasty.Toasty; -public class NotificationsSettingsFragment extends Fragment { +public class NotificationsSettingsFragment extends BaseNotificationsSettingsFragment { - private Context context; - private View notificationsElement; private View appUpdatesElement; private Switch appUpdatesSwitch; private View betaAppUpdatesElement; private Switch betaAppUpdatesSwitch; - /** - * Mandatory empty constructor for the fragment manager to instantiate the - * fragment (e.g. upon screen orientation changes). - */ - public NotificationsSettingsFragment() { - } - - public static NotificationsSettingsFragment newInstance() { - return new NotificationsSettingsFragment(); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.notification_settings_fragment, container, false); + View view = super.onCreateView(inflater, container, savedInstanceState); getViews(view); setDefaultStates(); setClickListeners(); - if (getActivity() != null) { - getActivity().setTitle(getString(R.string.notifications_pref_title)); - } - return view; } - @Override - public void onAttach(Context context) { - super.onAttach(context); - this.context = context; - } - private void getViews(View view) { - notificationsElement = view.findViewById(R.id.notifications); appUpdatesElement = view.findViewById(R.id.app_updates); appUpdatesSwitch = view.findViewById(R.id.app_updates_switch); betaAppUpdatesElement = view.findViewById(R.id.beta_app_updates); @@ -89,14 +55,7 @@ private void setDefaultStates() { } } - private void setClickListeners() { - - notificationsElement.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onNotificationsClicked(); - } - }); + protected void setClickListeners() { appUpdatesElement.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -131,24 +90,6 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { }); } - private void onNotificationsClicked() { - Intent intent = new Intent(); - intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); - - //for Android 5-7 - intent.putExtra("app_package", context.getPackageName()); - intent.putExtra("app_uid", context.getApplicationInfo().uid); - - // for Android O - intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName()); - - if (intent.resolveActivity(context.getPackageManager()) != null) { - startActivity(intent); - } else { - Toasty.error(context, "Couldn't find activity to start", Toast.LENGTH_SHORT, true).show(); - } - } - private void onAppUpdatesClicked(boolean isChecked) { if (isChecked) { FirebaseMessaging.getInstance().subscribeToTopic(getString(R.string.firebase_msg_app_updates_topic)); diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Settings/BaseLoggingSettingsFragment.java b/app/src/main/java/ca/pkay/rcloneexplorer/Settings/BaseLoggingSettingsFragment.java new file mode 100644 index 0000000..913602d --- /dev/null +++ b/app/src/main/java/ca/pkay/rcloneexplorer/Settings/BaseLoggingSettingsFragment.java @@ -0,0 +1,98 @@ +package ca.pkay.rcloneexplorer.Settings; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.Switch; + +import ca.pkay.rcloneexplorer.R; + +public abstract class BaseLoggingSettingsFragment extends Fragment { + + protected Context context; + private Switch useLogsSwitch; + private View useLogsElement; + + /** + * Mandatory empty constructor for the fragment manager to instantiate the + * fragment (e.g. upon screen orientation changes). + */ + public BaseLoggingSettingsFragment() { + } + + public static LoggingSettingsFragment newInstance() { + return new LoggingSettingsFragment(); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.logging_settings_fragment, container, false); + getViews(view); + setDefaultStates(); + setClickListeners(); + + if (getActivity() != null) { + getActivity().setTitle(getString(R.string.logging_settings_header)); + } + + return view; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + this.context = context; + } + + private void getViews(View view) { + useLogsSwitch = view.findViewById(R.id.use_logs_switch); + useLogsElement = view.findViewById(R.id.use_logs); + } + + private void setDefaultStates() { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + boolean useLogs = sharedPreferences.getBoolean(getString(R.string.pref_key_logs), false); + + useLogsSwitch.setChecked(useLogs); + } + + private void setClickListeners() { + useLogsElement.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (useLogsSwitch.isChecked()) { + useLogsSwitch.setChecked(false); + } else { + useLogsSwitch.setChecked(true); + } + } + }); + useLogsSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + onUseLogsClicked(isChecked); + } + }); + } + + private void onUseLogsClicked(boolean isChecked) { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(getString(R.string.pref_key_logs), isChecked); + editor.apply(); + } +} diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Settings/BaseNotificationsSettingsFragment.java b/app/src/main/java/ca/pkay/rcloneexplorer/Settings/BaseNotificationsSettingsFragment.java new file mode 100644 index 0000000..74e8464 --- /dev/null +++ b/app/src/main/java/ca/pkay/rcloneexplorer/Settings/BaseNotificationsSettingsFragment.java @@ -0,0 +1,88 @@ +package ca.pkay.rcloneexplorer.Settings; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import ca.pkay.rcloneexplorer.R; +import es.dmoral.toasty.Toasty; + +public abstract class BaseNotificationsSettingsFragment extends Fragment { + + protected Context context; + private View notificationsElement; + + /** + * Mandatory empty constructor for the fragment manager to instantiate the + * fragment (e.g. upon screen orientation changes). + */ + public BaseNotificationsSettingsFragment() { + } + + public static NotificationsSettingsFragment newInstance() { + return new NotificationsSettingsFragment(); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.notification_settings_fragment, container, false); + getViews(view); + setClickListeners(); + + if (getActivity() != null) { + getActivity().setTitle(getString(R.string.notifications_pref_title)); + } + + return view; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + this.context = context; + } + + private void getViews(View view) { + notificationsElement = view.findViewById(R.id.notifications); + } + + private void setClickListeners() { + notificationsElement.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onNotificationsClicked(); + } + }); + } + + private void onNotificationsClicked() { + Intent intent = new Intent(); + intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); + + //for Android 5-7 + intent.putExtra("app_package", context.getPackageName()); + intent.putExtra("app_uid", context.getApplicationInfo().uid); + + // for Android O + intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName()); + + if (intent.resolveActivity(context.getPackageManager()) != null) { + startActivity(intent); + } else { + Toasty.error(context, "Couldn't find activity to start", Toast.LENGTH_SHORT, true).show(); + } + } +} From a0cffa0b0d6ea7fefc4461350c6b6322456680bf Mon Sep 17 00:00:00 2001 From: Kai Winter Date: Sun, 7 Oct 2018 11:17:27 +0200 Subject: [PATCH 7/7] Moved common things to base class to reduce code duplicity --- .../ca/pkay/rcloneexplorer/MainActivity.java | 598 +---------------- .../ca/pkay/rcloneexplorer/MainActivity.java | 611 +---------------- .../pkay/rcloneexplorer/BaseMainActivity.java | 633 ++++++++++++++++++ 3 files changed, 650 insertions(+), 1192 deletions(-) create mode 100644 app/src/main/java/ca/pkay/rcloneexplorer/BaseMainActivity.java diff --git a/app/src/fdroid/java/ca/pkay/rcloneexplorer/MainActivity.java b/app/src/fdroid/java/ca/pkay/rcloneexplorer/MainActivity.java index bb4ae87..03f95d2 100644 --- a/app/src/fdroid/java/ca/pkay/rcloneexplorer/MainActivity.java +++ b/app/src/fdroid/java/ca/pkay/rcloneexplorer/MainActivity.java @@ -1,608 +1,20 @@ package ca.pkay.rcloneexplorer; -import android.Manifest; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.ActivityManager; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AlertDialog; -import android.support.design.widget.NavigationView; -import android.support.v4.view.GravityCompat; -import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; -import android.text.InputType; -import android.util.TypedValue; -import android.view.Menu; -import android.view.MenuItem; -import android.view.SubMenu; -import android.view.View; -import android.widget.Toast; - -import java.io.File; -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; - -import ca.pkay.rcloneexplorer.Dialogs.InputDialog; -import ca.pkay.rcloneexplorer.Dialogs.LoadingDialog; -import ca.pkay.rcloneexplorer.Fragments.FileExplorerFragment; -import ca.pkay.rcloneexplorer.Fragments.RemotesFragment; -import ca.pkay.rcloneexplorer.Items.RemoteItem; -import ca.pkay.rcloneexplorer.Settings.SettingsActivity; -import es.dmoral.toasty.Toasty; - -public class MainActivity extends AppCompatActivity - implements NavigationView.OnNavigationItemSelectedListener, - RemotesFragment.OnRemoteClickListener, - RemotesFragment.AddRemoteToNavDrawer, - InputDialog.OnPositive { - - private static final int READ_REQUEST_CODE = 42; // code when opening rclone config file - private static final int REQUEST_PERMISSION_CODE = 62; // code when requesting permissions - private static final int SETTINGS_CODE = 71; // code when coming back from settings - private static final int WRITE_REQUEST_CODE = 81; // code when exporting config - private final String FILE_EXPLORER_FRAGMENT_TAG = "ca.pkay.rcexplorer.MAIN_ACTIVITY_FILE_EXPLORER_TAG"; - private NavigationView navigationView; - private DrawerLayout drawer; - private Rclone rclone; - private Fragment fragment; - private Context context; - private Boolean isDarkTheme; - private HashMap drawerPinnedRemoteIds; - private int availableDrawerPinnedRemoteId; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - - applyTheme(); - context = this; - drawerPinnedRemoteIds = new HashMap<>(); - availableDrawerPinnedRemoteId = 2; - setContentView(R.layout.activity_main); - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - ActionBar actionbar = getSupportActionBar(); - if (actionbar != null) { - actionbar.setDisplayHomeAsUpEnabled(true); - actionbar.setHomeAsUpIndicator(R.drawable.ic_menu); - } - - drawer = findViewById(R.id.drawer_layout); - navigationView = findViewById(R.id.nav_view); - navigationView.setNavigationItemSelectedListener(this); - - requestPermissions(); - - rclone = new Rclone(this); - - findViewById(R.id.locked_config_btn).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - askForConfigPassword(); - } - }); - - Intent intent = getIntent(); - Bundle bundle = intent.getExtras(); - - int lastVersionCode = sharedPreferences.getInt(getString(R.string.pref_key_version_code), -1); - String lastVersionName = sharedPreferences.getString(getString(R.string.pref_key_version_name), ""); - int currentVersionCode = BuildConfig.VERSION_CODE; - String currentVersionName = BuildConfig.VERSION_NAME; - - if (!rclone.isRcloneBinaryCreated()) { - new CreateRcloneBinary().execute(); - } else if (lastVersionCode < currentVersionCode || !lastVersionName.equals(currentVersionName)) { - // In version code 24 there were changes to app shortcuts - // Remove this in the long future - if (lastVersionCode <= 23) { - AppShortcutsHelper.removeAllAppShortcuts(this); - AppShortcutsHelper.populateAppShortcuts(this, rclone.getRemotes()); - } - - new CreateRcloneBinary().execute(); - - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putInt(getString(R.string.pref_key_version_code), currentVersionCode); - editor.putString(getString(R.string.pref_key_version_name), currentVersionName); - editor.apply(); - } else if (rclone.isConfigEncrypted()) { - askForConfigPassword(); - } else if (savedInstanceState != null) { - fragment = getSupportFragmentManager().findFragmentByTag(FILE_EXPLORER_FRAGMENT_TAG); - if (fragment instanceof FileExplorerFragment) { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.flFragment, fragment, FILE_EXPLORER_FRAGMENT_TAG); - transaction.commit(); - } else { - startRemotesFragment(); - } - } else if (bundle != null && bundle.containsKey(AppShortcutsHelper.APP_SHORTCUT_REMOTE_NAME)) { - String remoteName = bundle.getString(AppShortcutsHelper.APP_SHORTCUT_REMOTE_NAME); - RemoteItem remoteItem = getRemoteItemFromName(remoteName); - if (remoteItem != null) { - AppShortcutsHelper.reportAppShortcutUsage(this, remoteItem.getName()); - startRemote(remoteItem, false); - } else { - Toasty.error(this, getString(R.string.remote_not_found), Toast.LENGTH_SHORT, true).show(); - finish(); - } - } else { - startRemotesFragment(); - } - } - - @Override - protected void onStart() { - super.onStart(); - pinRemotesToDrawer(); - } - - private void applyTheme() { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - int customPrimaryColor = sharedPreferences.getInt(getString(R.string.pref_key_color_primary), -1); - int customAccentColor = sharedPreferences.getInt(getString(R.string.pref_key_color_accent), -1); - isDarkTheme = sharedPreferences.getBoolean(getString(R.string.pref_key_dark_theme), false); - getTheme().applyStyle(CustomColorHelper.getPrimaryColorTheme(this, customPrimaryColor), true); - getTheme().applyStyle(CustomColorHelper.getAccentColorTheme(this, customAccentColor), true); - if (isDarkTheme) { - getTheme().applyStyle(R.style.DarkTheme, true); - } else { - getTheme().applyStyle(R.style.LightTheme, true); - } - - TypedValue typedValue = new TypedValue(); - getTheme().resolveAttribute(R.attr.colorPrimaryDark, typedValue, true); - getWindow().setStatusBarColor(typedValue.data); - - // set recents app color to the primary color - Bitmap bm = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round); - ActivityManager.TaskDescription taskDesc = new ActivityManager.TaskDescription(getString(R.string.app_name), bm, customPrimaryColor); - setTaskDescription(taskDesc); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home && !(fragment instanceof FileExplorerFragment)) { - drawer.openDrawer(GravityCompat.START); - return true; - } else { - return super.onOptionsItemSelected(item); - } - } - - public void openNavigationDrawer() { - drawer.openDrawer(GravityCompat.START); - } +public class MainActivity extends BaseMainActivity { @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - // result from file picker (for importing config file) - if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) { - Uri uri; - if (data != null) { - uri = data.getData(); - new CopyConfigFile().execute(uri); - } - } else if (requestCode == SETTINGS_CODE && resultCode == RESULT_OK) { - boolean themeChanged = data.getBooleanExtra(SettingsActivity.THEME_CHANGED, false); - if (themeChanged) { - recreate(); - } - } else if (requestCode == WRITE_REQUEST_CODE && resultCode == RESULT_OK) { - Uri uri; - if (data != null) { - uri = data.getData(); - try { - rclone.exportConfigFile(uri); - } catch (IOException e) { - e.printStackTrace(); - Toasty.error(this, getString(R.string.error_exporting_config_file), Toast.LENGTH_SHORT, true).show(); - } - } - } + protected boolean updateCheck() { + return false; } @Override - protected void onDestroy() { - super.onDestroy(); - File dir = getExternalCacheDir(); - if (dir != null && dir.isDirectory()) { - String[] children = dir.list(); - for (String aChildren : children) { - new File(dir, aChildren).delete(); - } - } - } + protected void checkEnableCrashReports(SharedPreferences sharedPreferences) { - @Override - public void onBackPressed() { - DrawerLayout drawer = findViewById(R.id.drawer_layout); - if (drawer.isDrawerOpen(GravityCompat.START)) { - drawer.closeDrawer(GravityCompat.START); - } else if (fragment != null && fragment instanceof FileExplorerFragment) { - if (((FileExplorerFragment) fragment).onBackButtonPressed()) { - return; - } else { - fragment = null; - } - } - super.onBackPressed(); } @Override - public boolean onNavigationItemSelected(@NonNull MenuItem item) { - // Handle navigation view item clicks here. - int id = item.getItemId(); - - if (drawerPinnedRemoteIds.containsKey(id)) { - startPinnedRemote(drawerPinnedRemoteIds.get(id)); - return true; - } - - switch (id) { - case R.id.nav_remotes: - startRemotesFragment(); - break; - case R.id.nav_import: - if (rclone.isConfigFileCreated()) { - warnUserAboutOverwritingConfiguration(); - } else { - importConfigFile(); - } - break; - case R.id.nav_export: - if (rclone.isConfigFileCreated()) { - exportConfigFile(); - } else { - Toasty.info(this, getString(R.string.no_config_found), Toast.LENGTH_SHORT, true).show(); - } - break; - case R.id.nav_settings: - Intent settingsIntent = new Intent(this, SettingsActivity.class); - startActivityForResult(settingsIntent, SETTINGS_CODE); - break; - case R.id.nav_about: - Intent aboutIntent = new Intent(this, AboutActivity.class); - startActivity(aboutIntent); - break; - } - - DrawerLayout drawer = findViewById(R.id.drawer_layout); - drawer.closeDrawer(GravityCompat.START); - return true; - } - - private void pinRemotesToDrawer() { - Menu menu = navigationView.getMenu(); - MenuItem existingMenu = menu.findItem(1); - if (existingMenu != null) { - return; - } - - SubMenu subMenu = menu.addSubMenu(R.id.drawer_pinned_header, 1, Menu.NONE, R.string.nav_drawer_pinned_header); - - List remoteItems = rclone.getRemotes(); - Collections.sort(remoteItems); - for (RemoteItem remoteItem : remoteItems) { - if (remoteItem.isDrawerPinned()) { - MenuItem menuItem = subMenu.add(R.id.nav_pinned, availableDrawerPinnedRemoteId, Menu.NONE, remoteItem.getName()); - drawerPinnedRemoteIds.put(availableDrawerPinnedRemoteId, remoteItem); - availableDrawerPinnedRemoteId++; - menuItem.setIcon(remoteItem.getRemoteIcon()); - } - } - } - - private void startRemotesFragment() { - fragment = RemotesFragment.newInstance(); - FragmentManager fragmentManager = getSupportFragmentManager(); - - for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) { - fragmentManager.popBackStack(); - } - - if (!isFinishing()) { - fragmentManager.beginTransaction().replace(R.id.flFragment, fragment).commitAllowingStateLoss(); - } - } - - private RemoteItem getRemoteItemFromName(String remoteName) { - List remoteItemList = rclone.getRemotes(); - for (RemoteItem remoteItem : remoteItemList) { - if (remoteItem.getName().equals(remoteName)) { - return remoteItem; - } - } - return null; - } - - private void warnUserAboutOverwritingConfiguration() { - AlertDialog.Builder builder; - if (isDarkTheme) { - builder = new AlertDialog.Builder(this, R.style.DarkDialogTheme); - } else { - builder = new AlertDialog.Builder(this); - } - builder.setTitle(R.string.replace_config_file_question); - builder.setMessage(R.string.config_file_lost_statement); - builder.setPositiveButton(R.string.continue_statement, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - dialogInterface.cancel(); - importConfigFile(); - } - }); - builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - dialogInterface.cancel(); - } - }); - builder.show(); - } - - private void askForConfigPassword() { - findViewById(R.id.locked_config).setVisibility(View.VISIBLE); - new InputDialog() - .setTitle(R.string.config_password_protected) - .setMessage(R.string.please_enter_password) - .setNegativeButton(R.string.cancel) - .setPositiveButton(R.string.okay_confirmation) - .setDarkTheme(isDarkTheme) - .setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD) - .show(getSupportFragmentManager(), "input dialog"); - } - - /* - * Input Dialog callback - */ - @Override - public void onPositive(String tag, String input) { - new DecryptConfig().execute(input); - } - - public void importConfigFile() { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - - startActivityForResult(intent, READ_REQUEST_CODE); - } - - public void exportConfigFile() { - Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("text/*"); - intent.putExtra(Intent.EXTRA_TITLE, "rclone.conf"); - startActivityForResult(intent, WRITE_REQUEST_CODE); - } - - public void requestPermissions() { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION_CODE); - } - } - - @Override - public void onRemoteClick(RemoteItem remote) { - startRemote(remote, true); - } - - private void startRemote(RemoteItem remote, boolean addToBackStack) { - fragment = FileExplorerFragment.newInstance(remote); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.flFragment, fragment, FILE_EXPLORER_FRAGMENT_TAG); - if (addToBackStack) { - transaction.addToBackStack(null); - } - transaction.commit(); - - AppShortcutsHelper.reportAppShortcutUsage(this, remote.getName()); - navigationView.getMenu().getItem(0).setChecked(false); - } - - private void startPinnedRemote(RemoteItem remoteItem) { - if (fragment != null && fragment instanceof FileExplorerFragment) { - FragmentManager fragmentManager = getSupportFragmentManager(); - - // this is the case when remote gets started from a shortcut - // therefore back should exit the app, and not go into remotes screen - if (fragmentManager.getBackStackEntryCount() == 0) { - startRemote(remoteItem, false); - } else { - for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) { - fragmentManager.popBackStack(); - } - - startRemote(remoteItem, true); - } - } else { - startRemote(remoteItem, true); - } - - DrawerLayout drawer = findViewById(R.id.drawer_layout); - drawer.closeDrawer(GravityCompat.START); - } - - @Override - public void addRemoteToNavDrawer() { - Menu menu = navigationView.getMenu(); - - // remove all items and add them again so that it's in alpha order - menu.removeItem(1); - drawerPinnedRemoteIds.clear(); - availableDrawerPinnedRemoteId = 1; - - pinRemotesToDrawer(); - } - - @Override - public void removeRemoteFromNavDrawer() { - Menu menu = navigationView.getMenu(); - - // remove all items and add them again so that it's in alpha order - menu.removeItem(1); - drawerPinnedRemoteIds.clear(); - availableDrawerPinnedRemoteId = 1; - - pinRemotesToDrawer(); - } - - @SuppressLint("StaticFieldLeak") - private class CreateRcloneBinary extends AsyncTask { - - private LoadingDialog loadingDialog; - - @Override - protected void onPreExecute() { - super.onPreExecute(); - loadingDialog = new LoadingDialog() - .setTitle(R.string.creating_rclone_binary) - .setCanCancel(false); - loadingDialog.show(getSupportFragmentManager(), "loading dialog"); - } - - @Override - protected Boolean doInBackground(Void... voids) { - try { - rclone.createRcloneBinary(); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - return true; - } - - @Override - protected void onPostExecute(Boolean success) { - super.onPostExecute(success); - if (!success) { - Toasty.error(context, getString(R.string.error_creating_rclone_binary), Toast.LENGTH_LONG, true).show(); - finish(); - System.exit(0); - } - if (loadingDialog.isStateSaved()) { - loadingDialog.dismissAllowingStateLoss(); - } else { - loadingDialog.dismiss(); - } - startRemotesFragment(); - } - } - - @SuppressLint("StaticFieldLeak") - private class CopyConfigFile extends AsyncTask { - - private LoadingDialog loadingDialog; - - @Override - protected void onPreExecute() { - super.onPreExecute(); - findViewById(R.id.locked_config).setVisibility(View.GONE); - loadingDialog = new LoadingDialog() - .setTitle(R.string.copying_rclone_config) - .setCanCancel(false); - loadingDialog.show(getSupportFragmentManager(), "loading dialog"); - } - - @Override - protected Boolean doInBackground(Uri... uris) { - try { - rclone.copyConfigFile(uris[0]); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - return true; - } - - @Override - protected void onPostExecute(Boolean success) { - super.onPostExecute(success); - if (loadingDialog.isStateSaved()) { - loadingDialog.dismissAllowingStateLoss(); - } else { - loadingDialog.dismiss(); - } - if (!success) { - return; - } - - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.remove(getString(R.string.shared_preferences_pinned_remotes)); - editor.remove(getString(R.string.shared_preferences_drawer_pinned_remotes)); - editor.remove(getString(R.string.shared_preferences_hidden_remotes)); - editor.apply(); - - if (rclone.isConfigEncrypted()) { - pinRemotesToDrawer(); // this will clear any previous pinned remotes - askForConfigPassword(); - } else { - AppShortcutsHelper.removeAllAppShortcuts(context); - AppShortcutsHelper.populateAppShortcuts(context, rclone.getRemotes()); - pinRemotesToDrawer(); - startRemotesFragment(); - } - } - } - - @SuppressLint("StaticFieldLeak") - private class DecryptConfig extends AsyncTask { - - private LoadingDialog loadingDialog; - - @Override - protected void onPreExecute() { - super.onPreExecute(); - loadingDialog = new LoadingDialog() - .setTitle(R.string.working) - .setCanCancel(false); - loadingDialog.show(getSupportFragmentManager(), "loading dialog"); - } - - @Override - protected Boolean doInBackground(String... strings) { - return rclone.decryptConfig(strings[0]); - } + protected void checkSubscribeToUpdates(SharedPreferences sharedPreferences) { - @Override - protected void onPostExecute(Boolean success) { - super.onPostExecute(success); - loadingDialog.dismiss(); - if (!success) { - Toasty.error(context, getString(R.string.error_unlocking_config), Toast.LENGTH_LONG, true).show(); - askForConfigPassword(); - } else { - findViewById(R.id.locked_config).setVisibility(View.GONE); - AppShortcutsHelper.removeAllAppShortcuts(context); - AppShortcutsHelper.populateAppShortcuts(context, rclone.getRemotes()); - startRemotesFragment(); - } - } } } diff --git a/app/src/google/java/ca/pkay/rcloneexplorer/MainActivity.java b/app/src/google/java/ca/pkay/rcloneexplorer/MainActivity.java index 2ea2b27..dbfc873 100644 --- a/app/src/google/java/ca/pkay/rcloneexplorer/MainActivity.java +++ b/app/src/google/java/ca/pkay/rcloneexplorer/MainActivity.java @@ -1,439 +1,50 @@ package ca.pkay.rcloneexplorer; -import android.Manifest; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.ActivityManager; -import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AlertDialog; -import android.support.design.widget.NavigationView; -import android.support.v4.view.GravityCompat; -import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; -import android.text.InputType; -import android.util.TypedValue; -import android.view.Menu; -import android.view.MenuItem; -import android.view.SubMenu; -import android.view.View; -import android.widget.Toast; import com.crashlytics.android.Crashlytics; import com.google.firebase.messaging.FirebaseMessaging; -import java.io.File; -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; - -import ca.pkay.rcloneexplorer.Dialogs.InputDialog; -import ca.pkay.rcloneexplorer.Dialogs.LoadingDialog; -import ca.pkay.rcloneexplorer.Fragments.FileExplorerFragment; -import ca.pkay.rcloneexplorer.Fragments.RemotesFragment; -import ca.pkay.rcloneexplorer.Items.RemoteItem; -import ca.pkay.rcloneexplorer.Settings.SettingsActivity; -import es.dmoral.toasty.Toasty; import io.fabric.sdk.android.Fabric; -public class MainActivity extends AppCompatActivity - implements NavigationView.OnNavigationItemSelectedListener, - RemotesFragment.OnRemoteClickListener, - RemotesFragment.AddRemoteToNavDrawer, - InputDialog.OnPositive { - - private static final int READ_REQUEST_CODE = 42; // code when opening rclone config file - private static final int REQUEST_PERMISSION_CODE = 62; // code when requesting permissions - private static final int SETTINGS_CODE = 71; // code when coming back from settings - private static final int WRITE_REQUEST_CODE = 81; // code when exporting config - private final String FILE_EXPLORER_FRAGMENT_TAG = "ca.pkay.rcexplorer.MAIN_ACTIVITY_FILE_EXPLORER_TAG"; - private NavigationView navigationView; - private DrawerLayout drawer; - private Rclone rclone; - private Fragment fragment; - private Context context; - private Boolean isDarkTheme; - private HashMap drawerPinnedRemoteIds; - private int availableDrawerPinnedRemoteId; +public class MainActivity extends BaseMainActivity { @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + protected boolean updateCheck() { if (getIntent() != null) { String s = getIntent().getStringExtra(getString(R.string.firebase_msg_app_updates_topic)); if (s != null && s.equals("true")) { openAppUpdate(); finish(); - return; + return true; } s = getIntent().getStringExtra(getString(R.string.firebase_msg_beta_app_updates_topic)); if (s != null) { openBetaUpdate(s); finish(); - return; + return true; } } - - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + return false; + } + + @Override + protected void checkEnableCrashReports(SharedPreferences sharedPreferences) { boolean enableCrashReports = sharedPreferences.getBoolean(getString(R.string.pref_key_crash_reports), false); if (enableCrashReports) { Fabric.with(this, new Crashlytics()); } + } - applyTheme(); - context = this; - drawerPinnedRemoteIds = new HashMap<>(); - availableDrawerPinnedRemoteId = 2; - setContentView(R.layout.activity_main); - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - ActionBar actionbar = getSupportActionBar(); - if (actionbar != null) { - actionbar.setDisplayHomeAsUpEnabled(true); - actionbar.setHomeAsUpIndicator(R.drawable.ic_menu); - } - - drawer = findViewById(R.id.drawer_layout); - navigationView = findViewById(R.id.nav_view); - navigationView.setNavigationItemSelectedListener(this); - - requestPermissions(); - - rclone = new Rclone(this); - - findViewById(R.id.locked_config_btn).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - askForConfigPassword(); - } - }); - + @Override + protected void checkSubscribeToUpdates(SharedPreferences sharedPreferences) { boolean appUpdates = sharedPreferences.getBoolean(getString(R.string.pref_key_app_updates), false); if (appUpdates) { FirebaseMessaging.getInstance().subscribeToTopic(getString(R.string.firebase_msg_app_updates_topic)); } - - Intent intent = getIntent(); - Bundle bundle = intent.getExtras(); - - int lastVersionCode = sharedPreferences.getInt(getString(R.string.pref_key_version_code), -1); - String lastVersionName = sharedPreferences.getString(getString(R.string.pref_key_version_name), ""); - int currentVersionCode = BuildConfig.VERSION_CODE; - String currentVersionName = BuildConfig.VERSION_NAME; - - if (!rclone.isRcloneBinaryCreated()) { - new CreateRcloneBinary().execute(); - } else if (lastVersionCode < currentVersionCode || !lastVersionName.equals(currentVersionName)) { - // In version code 24 there were changes to app shortcuts - // Remove this in the long future - if (lastVersionCode <= 23) { - AppShortcutsHelper.removeAllAppShortcuts(this); - AppShortcutsHelper.populateAppShortcuts(this, rclone.getRemotes()); - } - - new CreateRcloneBinary().execute(); - - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putInt(getString(R.string.pref_key_version_code), currentVersionCode); - editor.putString(getString(R.string.pref_key_version_name), currentVersionName); - editor.apply(); - } else if (rclone.isConfigEncrypted()) { - askForConfigPassword(); - } else if (savedInstanceState != null) { - fragment = getSupportFragmentManager().findFragmentByTag(FILE_EXPLORER_FRAGMENT_TAG); - if (fragment instanceof FileExplorerFragment) { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.flFragment, fragment, FILE_EXPLORER_FRAGMENT_TAG); - transaction.commit(); - } else { - startRemotesFragment(); - } - } else if (bundle != null && bundle.containsKey(AppShortcutsHelper.APP_SHORTCUT_REMOTE_NAME)) { - String remoteName = bundle.getString(AppShortcutsHelper.APP_SHORTCUT_REMOTE_NAME); - RemoteItem remoteItem = getRemoteItemFromName(remoteName); - if (remoteItem != null) { - AppShortcutsHelper.reportAppShortcutUsage(this, remoteItem.getName()); - startRemote(remoteItem, false); - } else { - Toasty.error(this, getString(R.string.remote_not_found), Toast.LENGTH_SHORT, true).show(); - finish(); - } - } else { - startRemotesFragment(); - } - } - - @Override - protected void onStart() { - super.onStart(); - pinRemotesToDrawer(); - } - - private void applyTheme() { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - int customPrimaryColor = sharedPreferences.getInt(getString(R.string.pref_key_color_primary), -1); - int customAccentColor = sharedPreferences.getInt(getString(R.string.pref_key_color_accent), -1); - isDarkTheme = sharedPreferences.getBoolean(getString(R.string.pref_key_dark_theme), false); - getTheme().applyStyle(CustomColorHelper.getPrimaryColorTheme(this, customPrimaryColor), true); - getTheme().applyStyle(CustomColorHelper.getAccentColorTheme(this, customAccentColor), true); - if (isDarkTheme) { - getTheme().applyStyle(R.style.DarkTheme, true); - } else { - getTheme().applyStyle(R.style.LightTheme, true); - } - - TypedValue typedValue = new TypedValue(); - getTheme().resolveAttribute(R.attr.colorPrimaryDark, typedValue, true); - getWindow().setStatusBarColor(typedValue.data); - - // set recents app color to the primary color - Bitmap bm = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round); - ActivityManager.TaskDescription taskDesc = new ActivityManager.TaskDescription(getString(R.string.app_name), bm, customPrimaryColor); - setTaskDescription(taskDesc); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home && !(fragment instanceof FileExplorerFragment)) { - drawer.openDrawer(GravityCompat.START); - return true; - } else { - return super.onOptionsItemSelected(item); - } - } - - public void openNavigationDrawer() { - drawer.openDrawer(GravityCompat.START); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - // result from file picker (for importing config file) - if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) { - Uri uri; - if (data != null) { - uri = data.getData(); - new CopyConfigFile().execute(uri); - } - } else if (requestCode == SETTINGS_CODE && resultCode == RESULT_OK) { - boolean themeChanged = data.getBooleanExtra(SettingsActivity.THEME_CHANGED, false); - if (themeChanged) { - recreate(); - } - } else if (requestCode == WRITE_REQUEST_CODE && resultCode == RESULT_OK) { - Uri uri; - if (data != null) { - uri = data.getData(); - try { - rclone.exportConfigFile(uri); - } catch (IOException e) { - e.printStackTrace(); - Toasty.error(this, getString(R.string.error_exporting_config_file), Toast.LENGTH_SHORT, true).show(); - } - } - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - File dir = getExternalCacheDir(); - if (dir != null && dir.isDirectory()) { - String[] children = dir.list(); - for (String aChildren : children) { - new File(dir, aChildren).delete(); - } - } - } - - @Override - public void onBackPressed() { - DrawerLayout drawer = findViewById(R.id.drawer_layout); - if (drawer.isDrawerOpen(GravityCompat.START)) { - drawer.closeDrawer(GravityCompat.START); - } else if (fragment != null && fragment instanceof FileExplorerFragment) { - if (((FileExplorerFragment) fragment).onBackButtonPressed()) { - return; - } else { - fragment = null; - } - } - super.onBackPressed(); - } - - @Override - public boolean onNavigationItemSelected(@NonNull MenuItem item) { - // Handle navigation view item clicks here. - int id = item.getItemId(); - - if (drawerPinnedRemoteIds.containsKey(id)) { - startPinnedRemote(drawerPinnedRemoteIds.get(id)); - return true; - } - - switch (id) { - case R.id.nav_remotes: - startRemotesFragment(); - break; - case R.id.nav_import: - if (rclone.isConfigFileCreated()) { - warnUserAboutOverwritingConfiguration(); - } else { - importConfigFile(); - } - break; - case R.id.nav_export: - if (rclone.isConfigFileCreated()) { - exportConfigFile(); - } else { - Toasty.info(this, getString(R.string.no_config_found), Toast.LENGTH_SHORT, true).show(); - } - break; - case R.id.nav_settings: - Intent settingsIntent = new Intent(this, SettingsActivity.class); - startActivityForResult(settingsIntent, SETTINGS_CODE); - break; - case R.id.nav_about: - Intent aboutIntent = new Intent(this, AboutActivity.class); - startActivity(aboutIntent); - break; - } - - DrawerLayout drawer = findViewById(R.id.drawer_layout); - drawer.closeDrawer(GravityCompat.START); - return true; - } - - private void pinRemotesToDrawer() { - Menu menu = navigationView.getMenu(); - MenuItem existingMenu = menu.findItem(1); - if (existingMenu != null) { - return; - } - - SubMenu subMenu = menu.addSubMenu(R.id.drawer_pinned_header, 1, Menu.NONE, R.string.nav_drawer_pinned_header); - - List remoteItems = rclone.getRemotes(); - Collections.sort(remoteItems); - for (RemoteItem remoteItem : remoteItems) { - if (remoteItem.isDrawerPinned()) { - MenuItem menuItem = subMenu.add(R.id.nav_pinned, availableDrawerPinnedRemoteId, Menu.NONE, remoteItem.getName()); - drawerPinnedRemoteIds.put(availableDrawerPinnedRemoteId, remoteItem); - availableDrawerPinnedRemoteId++; - menuItem.setIcon(remoteItem.getRemoteIcon()); - } - } - } - - private void startRemotesFragment() { - fragment = RemotesFragment.newInstance(); - FragmentManager fragmentManager = getSupportFragmentManager(); - - for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) { - fragmentManager.popBackStack(); - } - - if (!isFinishing()) { - fragmentManager.beginTransaction().replace(R.id.flFragment, fragment).commitAllowingStateLoss(); - } - } - - private RemoteItem getRemoteItemFromName(String remoteName) { - List remoteItemList = rclone.getRemotes(); - for (RemoteItem remoteItem : remoteItemList) { - if (remoteItem.getName().equals(remoteName)) { - return remoteItem; - } - } - return null; - } - - private void warnUserAboutOverwritingConfiguration() { - AlertDialog.Builder builder; - if (isDarkTheme) { - builder = new AlertDialog.Builder(this, R.style.DarkDialogTheme); - } else { - builder = new AlertDialog.Builder(this); - } - builder.setTitle(R.string.replace_config_file_question); - builder.setMessage(R.string.config_file_lost_statement); - builder.setPositiveButton(R.string.continue_statement, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - dialogInterface.cancel(); - importConfigFile(); - } - }); - builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - dialogInterface.cancel(); - } - }); - builder.show(); - } - - private void askForConfigPassword() { - findViewById(R.id.locked_config).setVisibility(View.VISIBLE); - new InputDialog() - .setTitle(R.string.config_password_protected) - .setMessage(R.string.please_enter_password) - .setNegativeButton(R.string.cancel) - .setPositiveButton(R.string.okay_confirmation) - .setDarkTheme(isDarkTheme) - .setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD) - .show(getSupportFragmentManager(), "input dialog"); - } - - /* - * Input Dialog callback - */ - @Override - public void onPositive(String tag, String input) { - new DecryptConfig().execute(input); - } - - public void importConfigFile() { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - - startActivityForResult(intent, READ_REQUEST_CODE); - } - - public void exportConfigFile() { - Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("text/*"); - intent.putExtra(Intent.EXTRA_TITLE, "rclone.conf"); - startActivityForResult(intent, WRITE_REQUEST_CODE); - } - - public void requestPermissions() { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION_CODE); - } } private void openAppUpdate() { @@ -448,202 +59,4 @@ private void openBetaUpdate(String url) { startActivity(intent); } - @Override - public void onRemoteClick(RemoteItem remote) { - startRemote(remote, true); - } - - private void startRemote(RemoteItem remote, boolean addToBackStack) { - fragment = FileExplorerFragment.newInstance(remote); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.flFragment, fragment, FILE_EXPLORER_FRAGMENT_TAG); - if (addToBackStack) { - transaction.addToBackStack(null); - } - transaction.commit(); - - AppShortcutsHelper.reportAppShortcutUsage(this, remote.getName()); - navigationView.getMenu().getItem(0).setChecked(false); - } - - private void startPinnedRemote(RemoteItem remoteItem) { - if (fragment != null && fragment instanceof FileExplorerFragment) { - FragmentManager fragmentManager = getSupportFragmentManager(); - - // this is the case when remote gets started from a shortcut - // therefore back should exit the app, and not go into remotes screen - if (fragmentManager.getBackStackEntryCount() == 0) { - startRemote(remoteItem, false); - } else { - for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) { - fragmentManager.popBackStack(); - } - - startRemote(remoteItem, true); - } - } else { - startRemote(remoteItem, true); - } - - DrawerLayout drawer = findViewById(R.id.drawer_layout); - drawer.closeDrawer(GravityCompat.START); - } - - @Override - public void addRemoteToNavDrawer() { - Menu menu = navigationView.getMenu(); - - // remove all items and add them again so that it's in alpha order - menu.removeItem(1); - drawerPinnedRemoteIds.clear(); - availableDrawerPinnedRemoteId = 1; - - pinRemotesToDrawer(); - } - - @Override - public void removeRemoteFromNavDrawer() { - Menu menu = navigationView.getMenu(); - - // remove all items and add them again so that it's in alpha order - menu.removeItem(1); - drawerPinnedRemoteIds.clear(); - availableDrawerPinnedRemoteId = 1; - - pinRemotesToDrawer(); - } - - @SuppressLint("StaticFieldLeak") - private class CreateRcloneBinary extends AsyncTask { - - private LoadingDialog loadingDialog; - - @Override - protected void onPreExecute() { - super.onPreExecute(); - loadingDialog = new LoadingDialog() - .setTitle(R.string.creating_rclone_binary) - .setCanCancel(false); - loadingDialog.show(getSupportFragmentManager(), "loading dialog"); - } - - @Override - protected Boolean doInBackground(Void... voids) { - try { - rclone.createRcloneBinary(); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - return true; - } - - @Override - protected void onPostExecute(Boolean success) { - super.onPostExecute(success); - if (!success) { - Toasty.error(context, getString(R.string.error_creating_rclone_binary), Toast.LENGTH_LONG, true).show(); - finish(); - System.exit(0); - } - if (loadingDialog.isStateSaved()) { - loadingDialog.dismissAllowingStateLoss(); - } else { - loadingDialog.dismiss(); - } - startRemotesFragment(); - } - } - - @SuppressLint("StaticFieldLeak") - private class CopyConfigFile extends AsyncTask { - - private LoadingDialog loadingDialog; - - @Override - protected void onPreExecute() { - super.onPreExecute(); - findViewById(R.id.locked_config).setVisibility(View.GONE); - loadingDialog = new LoadingDialog() - .setTitle(R.string.copying_rclone_config) - .setCanCancel(false); - loadingDialog.show(getSupportFragmentManager(), "loading dialog"); - } - - @Override - protected Boolean doInBackground(Uri... uris) { - try { - rclone.copyConfigFile(uris[0]); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - return true; - } - - @Override - protected void onPostExecute(Boolean success) { - super.onPostExecute(success); - if (loadingDialog.isStateSaved()) { - loadingDialog.dismissAllowingStateLoss(); - } else { - loadingDialog.dismiss(); - } - if (!success) { - return; - } - - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.remove(getString(R.string.shared_preferences_pinned_remotes)); - editor.remove(getString(R.string.shared_preferences_drawer_pinned_remotes)); - editor.remove(getString(R.string.shared_preferences_hidden_remotes)); - editor.apply(); - - if (rclone.isConfigEncrypted()) { - pinRemotesToDrawer(); // this will clear any previous pinned remotes - askForConfigPassword(); - } else { - AppShortcutsHelper.removeAllAppShortcuts(context); - AppShortcutsHelper.populateAppShortcuts(context, rclone.getRemotes()); - pinRemotesToDrawer(); - startRemotesFragment(); - } - } - } - - @SuppressLint("StaticFieldLeak") - private class DecryptConfig extends AsyncTask { - - private LoadingDialog loadingDialog; - - @Override - protected void onPreExecute() { - super.onPreExecute(); - loadingDialog = new LoadingDialog() - .setTitle(R.string.working) - .setCanCancel(false); - loadingDialog.show(getSupportFragmentManager(), "loading dialog"); - } - - @Override - protected Boolean doInBackground(String... strings) { - return rclone.decryptConfig(strings[0]); - } - - @Override - protected void onPostExecute(Boolean success) { - super.onPostExecute(success); - loadingDialog.dismiss(); - if (!success) { - Toasty.error(context, getString(R.string.error_unlocking_config), Toast.LENGTH_LONG, true).show(); - askForConfigPassword(); - } else { - findViewById(R.id.locked_config).setVisibility(View.GONE); - AppShortcutsHelper.removeAllAppShortcuts(context); - AppShortcutsHelper.populateAppShortcuts(context, rclone.getRemotes()); - startRemotesFragment(); - } - } - } } diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/BaseMainActivity.java b/app/src/main/java/ca/pkay/rcloneexplorer/BaseMainActivity.java new file mode 100644 index 0000000..fc965ed --- /dev/null +++ b/app/src/main/java/ca/pkay/rcloneexplorer/BaseMainActivity.java @@ -0,0 +1,633 @@ +package ca.pkay.rcloneexplorer; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.ActivityManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.design.widget.NavigationView; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.content.ContextCompat; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.text.InputType; +import android.util.TypedValue; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SubMenu; +import android.view.View; +import android.widget.Toast; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import ca.pkay.rcloneexplorer.Dialogs.InputDialog; +import ca.pkay.rcloneexplorer.Dialogs.LoadingDialog; +import ca.pkay.rcloneexplorer.Fragments.FileExplorerFragment; +import ca.pkay.rcloneexplorer.Fragments.RemotesFragment; +import ca.pkay.rcloneexplorer.Items.RemoteItem; +import ca.pkay.rcloneexplorer.Settings.SettingsActivity; +import es.dmoral.toasty.Toasty; + +public abstract class BaseMainActivity extends AppCompatActivity + implements NavigationView.OnNavigationItemSelectedListener, + RemotesFragment.OnRemoteClickListener, + RemotesFragment.AddRemoteToNavDrawer, + InputDialog.OnPositive { + + private static final int READ_REQUEST_CODE = 42; // code when opening rclone config file + private static final int REQUEST_PERMISSION_CODE = 62; // code when requesting permissions + private static final int SETTINGS_CODE = 71; // code when coming back from settings + private static final int WRITE_REQUEST_CODE = 81; // code when exporting config + private final String FILE_EXPLORER_FRAGMENT_TAG = "ca.pkay.rcexplorer.MAIN_ACTIVITY_FILE_EXPLORER_TAG"; + private NavigationView navigationView; + private DrawerLayout drawer; + private Rclone rclone; + private Fragment fragment; + private Context context; + private Boolean isDarkTheme; + private HashMap drawerPinnedRemoteIds; + private int availableDrawerPinnedRemoteId; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (updateCheck()) { + return; + } + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + checkEnableCrashReports(sharedPreferences); + + applyTheme(); + context = this; + drawerPinnedRemoteIds = new HashMap<>(); + availableDrawerPinnedRemoteId = 2; + setContentView(R.layout.activity_main); + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + ActionBar actionbar = getSupportActionBar(); + if (actionbar != null) { + actionbar.setDisplayHomeAsUpEnabled(true); + actionbar.setHomeAsUpIndicator(R.drawable.ic_menu); + } + + drawer = findViewById(R.id.drawer_layout); + navigationView = findViewById(R.id.nav_view); + navigationView.setNavigationItemSelectedListener(this); + + requestPermissions(); + + rclone = new Rclone(this); + + findViewById(R.id.locked_config_btn).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + askForConfigPassword(); + } + }); + + + checkSubscribeToUpdates(sharedPreferences); + + Intent intent = getIntent(); + Bundle bundle = intent.getExtras(); + + int lastVersionCode = sharedPreferences.getInt(getString(R.string.pref_key_version_code), -1); + String lastVersionName = sharedPreferences.getString(getString(R.string.pref_key_version_name), ""); + int currentVersionCode = BuildConfig.VERSION_CODE; + String currentVersionName = BuildConfig.VERSION_NAME; + + if (!rclone.isRcloneBinaryCreated()) { + new CreateRcloneBinary().execute(); + } else if (lastVersionCode < currentVersionCode || !lastVersionName.equals(currentVersionName)) { + // In version code 24 there were changes to app shortcuts + // Remove this in the long future + if (lastVersionCode <= 23) { + AppShortcutsHelper.removeAllAppShortcuts(this); + AppShortcutsHelper.populateAppShortcuts(this, rclone.getRemotes()); + } + + new CreateRcloneBinary().execute(); + + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putInt(getString(R.string.pref_key_version_code), currentVersionCode); + editor.putString(getString(R.string.pref_key_version_name), currentVersionName); + editor.apply(); + } else if (rclone.isConfigEncrypted()) { + askForConfigPassword(); + } else if (savedInstanceState != null) { + fragment = getSupportFragmentManager().findFragmentByTag(FILE_EXPLORER_FRAGMENT_TAG); + if (fragment instanceof FileExplorerFragment) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.flFragment, fragment, FILE_EXPLORER_FRAGMENT_TAG); + transaction.commit(); + } else { + startRemotesFragment(); + } + } else if (bundle != null && bundle.containsKey(AppShortcutsHelper.APP_SHORTCUT_REMOTE_NAME)) { + String remoteName = bundle.getString(AppShortcutsHelper.APP_SHORTCUT_REMOTE_NAME); + RemoteItem remoteItem = getRemoteItemFromName(remoteName); + if (remoteItem != null) { + AppShortcutsHelper.reportAppShortcutUsage(this, remoteItem.getName()); + startRemote(remoteItem, false); + } else { + Toasty.error(this, getString(R.string.remote_not_found), Toast.LENGTH_SHORT, true).show(); + finish(); + } + } else { + startRemotesFragment(); + } + } + + protected abstract boolean updateCheck(); + + protected abstract void checkEnableCrashReports(SharedPreferences sharedPreferences); + + protected abstract void checkSubscribeToUpdates(SharedPreferences sharedPreferences); + + @Override + protected void onStart() { + super.onStart(); + pinRemotesToDrawer(); + } + + private void applyTheme() { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + int customPrimaryColor = sharedPreferences.getInt(getString(R.string.pref_key_color_primary), -1); + int customAccentColor = sharedPreferences.getInt(getString(R.string.pref_key_color_accent), -1); + isDarkTheme = sharedPreferences.getBoolean(getString(R.string.pref_key_dark_theme), false); + getTheme().applyStyle(CustomColorHelper.getPrimaryColorTheme(this, customPrimaryColor), true); + getTheme().applyStyle(CustomColorHelper.getAccentColorTheme(this, customAccentColor), true); + if (isDarkTheme) { + getTheme().applyStyle(R.style.DarkTheme, true); + } else { + getTheme().applyStyle(R.style.LightTheme, true); + } + + TypedValue typedValue = new TypedValue(); + getTheme().resolveAttribute(R.attr.colorPrimaryDark, typedValue, true); + getWindow().setStatusBarColor(typedValue.data); + + // set recents app color to the primary color + Bitmap bm = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round); + ActivityManager.TaskDescription taskDesc = new ActivityManager.TaskDescription(getString(R.string.app_name), bm, customPrimaryColor); + setTaskDescription(taskDesc); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home && !(fragment instanceof FileExplorerFragment)) { + drawer.openDrawer(GravityCompat.START); + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + public void openNavigationDrawer() { + drawer.openDrawer(GravityCompat.START); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + // result from file picker (for importing config file) + if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) { + Uri uri; + if (data != null) { + uri = data.getData(); + new CopyConfigFile().execute(uri); + } + } else if (requestCode == SETTINGS_CODE && resultCode == RESULT_OK) { + boolean themeChanged = data.getBooleanExtra(SettingsActivity.THEME_CHANGED, false); + if (themeChanged) { + recreate(); + } + } else if (requestCode == WRITE_REQUEST_CODE && resultCode == RESULT_OK) { + Uri uri; + if (data != null) { + uri = data.getData(); + try { + rclone.exportConfigFile(uri); + } catch (IOException e) { + e.printStackTrace(); + Toasty.error(this, getString(R.string.error_exporting_config_file), Toast.LENGTH_SHORT, true).show(); + } + } + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + File dir = getExternalCacheDir(); + if (dir != null && dir.isDirectory()) { + String[] children = dir.list(); + for (String aChildren : children) { + new File(dir, aChildren).delete(); + } + } + } + + @Override + public void onBackPressed() { + DrawerLayout drawer = findViewById(R.id.drawer_layout); + if (drawer.isDrawerOpen(GravityCompat.START)) { + drawer.closeDrawer(GravityCompat.START); + } else if (fragment != null && fragment instanceof FileExplorerFragment) { + if (((FileExplorerFragment) fragment).onBackButtonPressed()) { + return; + } else { + fragment = null; + } + } + super.onBackPressed(); + } + + @Override + public boolean onNavigationItemSelected(@NonNull MenuItem item) { + // Handle navigation view item clicks here. + int id = item.getItemId(); + + if (drawerPinnedRemoteIds.containsKey(id)) { + startPinnedRemote(drawerPinnedRemoteIds.get(id)); + return true; + } + + switch (id) { + case R.id.nav_remotes: + startRemotesFragment(); + break; + case R.id.nav_import: + if (rclone.isConfigFileCreated()) { + warnUserAboutOverwritingConfiguration(); + } else { + importConfigFile(); + } + break; + case R.id.nav_export: + if (rclone.isConfigFileCreated()) { + exportConfigFile(); + } else { + Toasty.info(this, getString(R.string.no_config_found), Toast.LENGTH_SHORT, true).show(); + } + break; + case R.id.nav_settings: + Intent settingsIntent = new Intent(this, SettingsActivity.class); + startActivityForResult(settingsIntent, SETTINGS_CODE); + break; + case R.id.nav_about: + Intent aboutIntent = new Intent(this, AboutActivity.class); + startActivity(aboutIntent); + break; + } + + DrawerLayout drawer = findViewById(R.id.drawer_layout); + drawer.closeDrawer(GravityCompat.START); + return true; + } + + private void pinRemotesToDrawer() { + Menu menu = navigationView.getMenu(); + MenuItem existingMenu = menu.findItem(1); + if (existingMenu != null) { + return; + } + + SubMenu subMenu = menu.addSubMenu(R.id.drawer_pinned_header, 1, Menu.NONE, R.string.nav_drawer_pinned_header); + + List remoteItems = rclone.getRemotes(); + Collections.sort(remoteItems); + for (RemoteItem remoteItem : remoteItems) { + if (remoteItem.isDrawerPinned()) { + MenuItem menuItem = subMenu.add(R.id.nav_pinned, availableDrawerPinnedRemoteId, Menu.NONE, remoteItem.getName()); + drawerPinnedRemoteIds.put(availableDrawerPinnedRemoteId, remoteItem); + availableDrawerPinnedRemoteId++; + menuItem.setIcon(remoteItem.getRemoteIcon()); + } + } + } + + private void startRemotesFragment() { + fragment = RemotesFragment.newInstance(); + FragmentManager fragmentManager = getSupportFragmentManager(); + + for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) { + fragmentManager.popBackStack(); + } + + if (!isFinishing()) { + fragmentManager.beginTransaction().replace(R.id.flFragment, fragment).commitAllowingStateLoss(); + } + } + + private RemoteItem getRemoteItemFromName(String remoteName) { + List remoteItemList = rclone.getRemotes(); + for (RemoteItem remoteItem : remoteItemList) { + if (remoteItem.getName().equals(remoteName)) { + return remoteItem; + } + } + return null; + } + + private void warnUserAboutOverwritingConfiguration() { + AlertDialog.Builder builder; + if (isDarkTheme) { + builder = new AlertDialog.Builder(this, R.style.DarkDialogTheme); + } else { + builder = new AlertDialog.Builder(this); + } + builder.setTitle(R.string.replace_config_file_question); + builder.setMessage(R.string.config_file_lost_statement); + builder.setPositiveButton(R.string.continue_statement, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + dialogInterface.cancel(); + importConfigFile(); + } + }); + builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + dialogInterface.cancel(); + } + }); + builder.show(); + } + + private void askForConfigPassword() { + findViewById(R.id.locked_config).setVisibility(View.VISIBLE); + new InputDialog() + .setTitle(R.string.config_password_protected) + .setMessage(R.string.please_enter_password) + .setNegativeButton(R.string.cancel) + .setPositiveButton(R.string.okay_confirmation) + .setDarkTheme(isDarkTheme) + .setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD) + .show(getSupportFragmentManager(), "input dialog"); + } + + /* + * Input Dialog callback + */ + @Override + public void onPositive(String tag, String input) { + new DecryptConfig().execute(input); + } + + public void importConfigFile() { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + + startActivityForResult(intent, READ_REQUEST_CODE); + } + + public void exportConfigFile() { + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("text/*"); + intent.putExtra(Intent.EXTRA_TITLE, "rclone.conf"); + startActivityForResult(intent, WRITE_REQUEST_CODE); + } + + public void requestPermissions() { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION_CODE); + } + } + + private void openAppUpdate() { + Uri uri = Uri.parse(getString(R.string.app_latest_release_url)); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivity(intent); + } + + private void openBetaUpdate(String url) { + Uri uri = Uri.parse(url); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivity(intent); + } + + @Override + public void onRemoteClick(RemoteItem remote) { + startRemote(remote, true); + } + + private void startRemote(RemoteItem remote, boolean addToBackStack) { + fragment = FileExplorerFragment.newInstance(remote); + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.flFragment, fragment, FILE_EXPLORER_FRAGMENT_TAG); + if (addToBackStack) { + transaction.addToBackStack(null); + } + transaction.commit(); + + AppShortcutsHelper.reportAppShortcutUsage(this, remote.getName()); + navigationView.getMenu().getItem(0).setChecked(false); + } + + private void startPinnedRemote(RemoteItem remoteItem) { + if (fragment != null && fragment instanceof FileExplorerFragment) { + FragmentManager fragmentManager = getSupportFragmentManager(); + + // this is the case when remote gets started from a shortcut + // therefore back should exit the app, and not go into remotes screen + if (fragmentManager.getBackStackEntryCount() == 0) { + startRemote(remoteItem, false); + } else { + for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) { + fragmentManager.popBackStack(); + } + + startRemote(remoteItem, true); + } + } else { + startRemote(remoteItem, true); + } + + DrawerLayout drawer = findViewById(R.id.drawer_layout); + drawer.closeDrawer(GravityCompat.START); + } + + @Override + public void addRemoteToNavDrawer() { + Menu menu = navigationView.getMenu(); + + // remove all items and add them again so that it's in alpha order + menu.removeItem(1); + drawerPinnedRemoteIds.clear(); + availableDrawerPinnedRemoteId = 1; + + pinRemotesToDrawer(); + } + + @Override + public void removeRemoteFromNavDrawer() { + Menu menu = navigationView.getMenu(); + + // remove all items and add them again so that it's in alpha order + menu.removeItem(1); + drawerPinnedRemoteIds.clear(); + availableDrawerPinnedRemoteId = 1; + + pinRemotesToDrawer(); + } + + @SuppressLint("StaticFieldLeak") + private class CreateRcloneBinary extends AsyncTask { + + private LoadingDialog loadingDialog; + + @Override + protected void onPreExecute() { + super.onPreExecute(); + loadingDialog = new LoadingDialog() + .setTitle(R.string.creating_rclone_binary) + .setCanCancel(false); + loadingDialog.show(getSupportFragmentManager(), "loading dialog"); + } + + @Override + protected Boolean doInBackground(Void... voids) { + try { + rclone.createRcloneBinary(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + return true; + } + + @Override + protected void onPostExecute(Boolean success) { + super.onPostExecute(success); + if (!success) { + Toasty.error(context, getString(R.string.error_creating_rclone_binary), Toast.LENGTH_LONG, true).show(); + finish(); + System.exit(0); + } + if (loadingDialog.isStateSaved()) { + loadingDialog.dismissAllowingStateLoss(); + } else { + loadingDialog.dismiss(); + } + startRemotesFragment(); + } + } + + @SuppressLint("StaticFieldLeak") + private class CopyConfigFile extends AsyncTask { + + private LoadingDialog loadingDialog; + + @Override + protected void onPreExecute() { + super.onPreExecute(); + findViewById(R.id.locked_config).setVisibility(View.GONE); + loadingDialog = new LoadingDialog() + .setTitle(R.string.copying_rclone_config) + .setCanCancel(false); + loadingDialog.show(getSupportFragmentManager(), "loading dialog"); + } + + @Override + protected Boolean doInBackground(Uri... uris) { + try { + rclone.copyConfigFile(uris[0]); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + return true; + } + + @Override + protected void onPostExecute(Boolean success) { + super.onPostExecute(success); + if (loadingDialog.isStateSaved()) { + loadingDialog.dismissAllowingStateLoss(); + } else { + loadingDialog.dismiss(); + } + if (!success) { + return; + } + + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.remove(getString(R.string.shared_preferences_pinned_remotes)); + editor.remove(getString(R.string.shared_preferences_drawer_pinned_remotes)); + editor.remove(getString(R.string.shared_preferences_hidden_remotes)); + editor.apply(); + + if (rclone.isConfigEncrypted()) { + pinRemotesToDrawer(); // this will clear any previous pinned remotes + askForConfigPassword(); + } else { + AppShortcutsHelper.removeAllAppShortcuts(context); + AppShortcutsHelper.populateAppShortcuts(context, rclone.getRemotes()); + pinRemotesToDrawer(); + startRemotesFragment(); + } + } + } + + @SuppressLint("StaticFieldLeak") + private class DecryptConfig extends AsyncTask { + + private LoadingDialog loadingDialog; + + @Override + protected void onPreExecute() { + super.onPreExecute(); + loadingDialog = new LoadingDialog() + .setTitle(R.string.working) + .setCanCancel(false); + loadingDialog.show(getSupportFragmentManager(), "loading dialog"); + } + + @Override + protected Boolean doInBackground(String... strings) { + return rclone.decryptConfig(strings[0]); + } + + @Override + protected void onPostExecute(Boolean success) { + super.onPostExecute(success); + loadingDialog.dismiss(); + if (!success) { + Toasty.error(context, getString(R.string.error_unlocking_config), Toast.LENGTH_LONG, true).show(); + askForConfigPassword(); + } else { + findViewById(R.id.locked_config).setVisibility(View.GONE); + AppShortcutsHelper.removeAllAppShortcuts(context); + AppShortcutsHelper.populateAppShortcuts(context, rclone.getRemotes()); + startRemotesFragment(); + } + } + } +}