/* * Nextcloud Android client application * * @author Andy Scherzinger * Copyright (C) 2016 Andy Scherzinger * Copyright (C) 2016 Nextcloud * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; either * version 3 of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU AFFERO GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Affero General Public * License along with this program. If not, see . */ package com.owncloud.android.ui.activity; import android.accounts.Account; import android.annotation.SuppressLint; import android.app.Activity; import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.PowerManager; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.BuildConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.MediaFolder; import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.MediaProvider; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.jobs.MediaFoldersDetectionJob; import com.owncloud.android.jobs.NotificationJob; import com.owncloud.android.ui.adapter.SyncedFolderAdapter; import com.owncloud.android.ui.decoration.MediaGridItemDecoration; import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment; import com.owncloud.android.ui.dialog.parcel.SyncedFolderParcelable; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.FilesSyncHelper; import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.ThemeUtils; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.inject.Inject; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS; import static com.owncloud.android.datamodel.SyncedFolderDisplayItem.UNPERSISTED_ID; /** * Activity displaying all auto-synced folders and/or instant upload media folders. */ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderAdapter.ClickListener, SyncedFolderPreferencesDialogFragment.OnSyncedFolderPreferenceListener, Injectable { private static final String[] PRIORITIZED_FOLDERS = new String[]{"Camera", "Screenshots"}; public static final String EXTRA_SHOW_SIDEBAR = "SHOW_SIDEBAR"; private static final String SYNCED_FOLDER_PREFERENCES_DIALOG_TAG = "SYNCED_FOLDER_PREFERENCES_DIALOG"; private static final String TAG = SyncedFoldersActivity.class.getSimpleName(); private RecyclerView mRecyclerView; private SyncedFolderAdapter mAdapter; private LinearLayout mProgress; private TextView mEmpty; private SyncedFolderProvider mSyncedFolderProvider; private SyncedFolderPreferencesDialogFragment mSyncedFolderPreferencesDialogFragment; private boolean showSidebar = true; private String path; private int type; @Inject AppPreferences preferences; @Inject PowerManagementService powerManagementService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getIntent().getExtras() != null) { showSidebar = getIntent().getExtras().getBoolean(EXTRA_SHOW_SIDEBAR); } setContentView(R.layout.synced_folders_layout); String account; Account currentAccount; if (getIntent() != null && getIntent().getExtras() != null) { account = getIntent().getExtras().getString(NotificationJob.KEY_NOTIFICATION_ACCOUNT); currentAccount = getAccount(); if (account != null && currentAccount != null && !account.equalsIgnoreCase(currentAccount.name)) { accountManager.setCurrentOwnCloudAccount(account); setAccount(getUserAccountManager().getCurrentAccount(), false); } path = getIntent().getStringExtra(MediaFoldersDetectionJob.KEY_MEDIA_FOLDER_PATH); type = getIntent().getIntExtra(MediaFoldersDetectionJob.KEY_MEDIA_FOLDER_TYPE, -1); // Cancel notification int notificationId = getIntent().getIntExtra(MediaFoldersDetectionJob.NOTIFICATION_ID, 0); NotificationManager notificationManager = (NotificationManager) getSystemService(Activity.NOTIFICATION_SERVICE); notificationManager.cancel(notificationId); } // setup toolbar setupToolbar(); if (getSupportActionBar() != null){ getSupportActionBar().setTitle(R.string.drawer_synced_folders); } // setup drawer setupDrawer(R.id.nav_synced_folders); if (!showSidebar) { setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); mDrawerToggle.setDrawerIndicatorEnabled(false); } setupContent(); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { ThemeUtils.setColoredTitle(getSupportActionBar(), getString(R.string.drawer_synced_folders), this); actionBar.setDisplayHomeAsUpEnabled(true); } if (ThemeUtils.themingEnabled(this)) { setTheme(R.style.FallbackThemingTheme); } } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.activity_synced_folders, menu); if (powerManagementService.isPowerSavingExclusionAvailable()) { MenuItem item = menu.findItem(R.id.action_disable_power_save_check); item.setVisible(true); item.setChecked(preferences.isPowerCheckDisabled()); item.setOnMenuItemClickListener(this::onDisablePowerSaveCheckClicked); } return true; } private boolean onDisablePowerSaveCheckClicked(MenuItem powerCheck) { if (!powerCheck.isChecked()) { showPowerCheckDialog(); } preferences.setPowerCheckDisabled(!powerCheck.isChecked()); powerCheck.setChecked(!powerCheck.isChecked()); return true; } private void showPowerCheckDialog() { AlertDialog alertDialog = new AlertDialog.Builder(this) .setView(findViewById(R.id.root_layout)) .setPositiveButton(R.string.common_ok, (dialog, which) -> dialog.dismiss()) .setTitle(ThemeUtils.getColoredTitle(getResources().getString(R.string.autoupload_disable_power_save_check), ThemeUtils.primaryAccentColor(this))) .setMessage(getString(R.string.power_save_check_dialog_message)) .show(); alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(ThemeUtils.primaryAccentColor(this)); } /** * sets up the UI elements and loads all media/synced folders. */ private void setupContent() { mRecyclerView = findViewById(android.R.id.list); mProgress = findViewById(android.R.id.progress); mEmpty = findViewById(android.R.id.empty); final int gridWidth = getResources().getInteger(R.integer.media_grid_width); boolean lightVersion = getResources().getBoolean(R.bool.syncedFolder_light); mAdapter = new SyncedFolderAdapter(this, gridWidth, this, lightVersion); mSyncedFolderProvider = new SyncedFolderProvider(getContentResolver(), preferences); final GridLayoutManager lm = new GridLayoutManager(this, gridWidth); mAdapter.setLayoutManager(lm); int spacing = getResources().getDimensionPixelSize(R.dimen.media_grid_spacing); mRecyclerView.addItemDecoration(new MediaGridItemDecoration(spacing)); mRecyclerView.setLayoutManager(lm); mRecyclerView.setAdapter(mAdapter); load(gridWidth * 2, false); } /** * loads all media/synced folders, adds them to the recycler view adapter and shows the list. * * @param perFolderMediaItemLimit the amount of media items to be loaded/shown per media folder */ private void load(final int perFolderMediaItemLimit, boolean force) { if (mAdapter.getItemCount() > 0 && !force) { return; } setListShown(false); final List mediaFolders = MediaProvider.getImageFolders(getContentResolver(), perFolderMediaItemLimit, this, false); mediaFolders.addAll(MediaProvider.getVideoFolders(getContentResolver(), perFolderMediaItemLimit, this, false)); List syncedFolderArrayList = mSyncedFolderProvider.getSyncedFolders(); List currentAccountSyncedFoldersList = new ArrayList<>(); Account currentAccount = getUserAccountManager().getCurrentAccount(); for (SyncedFolder syncedFolder : syncedFolderArrayList) { if (currentAccount != null && syncedFolder.getAccount().equals(currentAccount.name)) { // delete non-existing & disabled synced folders if (!new File(syncedFolder.getLocalPath()).exists() && !syncedFolder.isEnabled()) { mSyncedFolderProvider.deleteSyncedFolder(syncedFolder.getId()); } else { currentAccountSyncedFoldersList.add(syncedFolder); } } } List syncFolderItems = sortSyncedFolderItems( mergeFolderData(currentAccountSyncedFoldersList, mediaFolders)); mAdapter.setSyncFolderItems(syncFolderItems); mAdapter.notifyDataSetChanged(); setListShown(true); if (!TextUtils.isEmpty(path)) { int section = mAdapter.getSectionByLocalPathAndType(path, type); if (section >= 0) { onSyncFolderSettingsClick(section, mAdapter.get(section)); } } } /** * Sorts list of {@link SyncedFolderDisplayItem}s. * * @param syncFolderItemList list of items to be sorted * @return sorted list of items */ public static List sortSyncedFolderItems(List syncFolderItemList) { Collections.sort(syncFolderItemList, (f1, f2) -> { if (f1 == null && f2 == null) { return 0; } else if (f1 == null) { return -1; } else if (f2 == null) { return 1; } else if (f1.isEnabled() && f2.isEnabled()) { if (f1.getFolderName() == null) { return -1; } if (f2.getFolderName() == null) { return 1; } return f1.getFolderName().toLowerCase(Locale.getDefault()).compareTo( f2.getFolderName().toLowerCase(Locale.getDefault())); } else if (f1.getFolderName() == null && f2.getFolderName() == null) { return 0; } else if (f1.isEnabled()) { return -1; } else if (f2.isEnabled()) { return 1; } else if (f1.getFolderName() == null) { return -1; } else if (f2.getFolderName() == null) { return 1; } for (String folder : PRIORITIZED_FOLDERS) { if (folder.equals(f1.getFolderName()) && folder.equals(f2.getFolderName())) { return 0; } else if (folder.equals(f1.getFolderName())) { return -1; } else if (folder.equals(f2.getFolderName())) { return 1; } } return f1.getFolderName().toLowerCase(Locale.getDefault()).compareTo( f2.getFolderName().toLowerCase(Locale.getDefault())); }); return syncFolderItemList; } /** * merges two lists of {@link SyncedFolder} and {@link MediaFolder} items into one of SyncedFolderItems. * * @param syncedFolders the synced folders * @param mediaFolders the media folders * @return the merged list of SyncedFolderItems */ @NonNull private List mergeFolderData(List syncedFolders, @NonNull List mediaFolders) { Map syncedFoldersMap = createSyncedFoldersMap(syncedFolders); List result = new ArrayList<>(); for (MediaFolder mediaFolder : mediaFolders) { if (syncedFoldersMap.containsKey(mediaFolder.absolutePath + "-" + mediaFolder.type)) { SyncedFolder syncedFolder = syncedFoldersMap.get(mediaFolder.absolutePath + "-" + mediaFolder.type); syncedFoldersMap.remove(mediaFolder.absolutePath + "-" + mediaFolder.type); if (MediaFolderType.CUSTOM == syncedFolder.getType()) { result.add(createSyncedFolderWithoutMediaFolder(syncedFolder)); } else { result.add(createSyncedFolder(syncedFolder, mediaFolder)); } } else { result.add(createSyncedFolderFromMediaFolder(mediaFolder)); } } for (SyncedFolder syncedFolder : syncedFoldersMap.values()) { result.add(createSyncedFolderWithoutMediaFolder(syncedFolder)); } return result; } @NonNull private SyncedFolderDisplayItem createSyncedFolderWithoutMediaFolder(@NonNull SyncedFolder syncedFolder) { File localFolder = new File(syncedFolder.getLocalPath()); File[] files = getFileList(localFolder); List filePaths = getDisplayFilePathList(files); return new SyncedFolderDisplayItem( syncedFolder.getId(), syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder.getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.isEnabled(), filePaths, localFolder.getName(), files.length, syncedFolder.getType()); } /** * creates a SyncedFolderDisplayItem merging a {@link SyncedFolder} and a {@link MediaFolder} object instance. * * @param syncedFolder the synced folder object * @param mediaFolder the media folder object * @return the created SyncedFolderDisplayItem */ @NonNull private SyncedFolderDisplayItem createSyncedFolder(@NonNull SyncedFolder syncedFolder, @NonNull MediaFolder mediaFolder) { return new SyncedFolderDisplayItem( syncedFolder.getId(), syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder.getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.isEnabled(), mediaFolder.filePaths, mediaFolder.folderName, mediaFolder.numberOfFiles, mediaFolder.type); } /** * creates a {@link SyncedFolderDisplayItem} based on a {@link MediaFolder} object instance. * * @param mediaFolder the media folder object * @return the created SyncedFolderDisplayItem */ @NonNull private SyncedFolderDisplayItem createSyncedFolderFromMediaFolder(@NonNull MediaFolder mediaFolder) { return new SyncedFolderDisplayItem( UNPERSISTED_ID, mediaFolder.absolutePath, getString(R.string.instant_upload_path) + "/" + mediaFolder.folderName, true, false, false, getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, false, mediaFolder.filePaths, mediaFolder.folderName, mediaFolder.numberOfFiles, mediaFolder.type); } private File[] getFileList(File localFolder) { File[] files = localFolder.listFiles(pathname -> !pathname.isDirectory()); if (files != null) { Arrays.sort(files, (f1, f2) -> Long.compare(f1.lastModified(), f2.lastModified())); } else { files = new File[]{}; } return files; } private List getDisplayFilePathList(File... files) { List filePaths = null; if (files != null && files.length > 0) { filePaths = new ArrayList<>(); for (int i = 0; i < 7 && i < files.length; i++) { filePaths.add(files[i].getAbsolutePath()); } } return filePaths; } /** * creates a lookup map for a list of given {@link SyncedFolder}s with their local path as the key. * * @param syncFolders list of {@link SyncedFolder}s * @return the lookup map for {@link SyncedFolder}s */ @NonNull private Map createSyncedFoldersMap(List syncFolders) { Map result = new HashMap<>(); if (syncFolders != null) { for (SyncedFolder syncFolder : syncFolders) { result.put(syncFolder.getLocalPath() + "-" + syncFolder.getType(), syncFolder); } } return result; } /** * show/hide recycler view list or the empty message / progress info. * * @param shown flag if list should be shown */ private void setListShown(boolean shown) { if (mRecyclerView != null) { mRecyclerView.setVisibility(shown ? View.VISIBLE : View.GONE); mProgress.setVisibility(shown ? View.GONE : View.VISIBLE); mEmpty.setVisibility(shown && mAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); } } @Override public boolean onOptionsItemSelected(MenuItem item) { boolean result = true; switch (item.getItemId()) { case android.R.id.home: { if (showSidebar) { if (isDrawerOpen()) { closeDrawer(); } else { openDrawer(); } } else { Intent settingsIntent = new Intent(getApplicationContext(), SettingsActivity.class); startActivity(settingsIntent); } break; } case R.id.action_create_custom_folder: { Log.d(TAG, "Show custom folder dialog"); SyncedFolderDisplayItem emptyCustomFolder = new SyncedFolderDisplayItem( SyncedFolder.UNPERSISTED_ID, null, null, true, false, false, getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, false, null, MediaFolderType.CUSTOM); onSyncFolderSettingsClick(0, emptyCustomFolder); } default: result = super.onOptionsItemSelected(item); break; } return result; } @Override public void restart() { Intent i = new Intent(this, FileDisplayActivity.class); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i); } @Override public void showFiles(boolean onDeviceOnly) { MainApp.showOnlyFilesOnDevice(onDeviceOnly); Intent fileDisplayActivity = new Intent(getApplicationContext(), FileDisplayActivity.class); fileDisplayActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(fileDisplayActivity); } @Override public void onSyncStatusToggleClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem) { ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext(). getContentResolver()); if (syncedFolderDisplayItem.getId() > UNPERSISTED_ID) { mSyncedFolderProvider.updateSyncedFolderEnabled(syncedFolderDisplayItem.getId(), syncedFolderDisplayItem.isEnabled()); } else { long storedId = mSyncedFolderProvider.storeSyncedFolder(syncedFolderDisplayItem); if (storedId != -1) { syncedFolderDisplayItem.setId(storedId); } } if (syncedFolderDisplayItem.isEnabled()) { FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolderDisplayItem); showBatteryOptimizationInfo(); } else { String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + syncedFolderDisplayItem.getId(); arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey); } } @Override public void onSyncFolderSettingsClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem) { FragmentManager fm = getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); ft.addToBackStack(null); mSyncedFolderPreferencesDialogFragment = SyncedFolderPreferencesDialogFragment.newInstance( syncedFolderDisplayItem, section); mSyncedFolderPreferencesDialogFragment.show(ft, SYNCED_FOLDER_PREFERENCES_DIALOG_TAG); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == SyncedFolderPreferencesDialogFragment.REQUEST_CODE__SELECT_REMOTE_FOLDER && resultCode == RESULT_OK && mSyncedFolderPreferencesDialogFragment != null) { OCFile chosenFolder = data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER); mSyncedFolderPreferencesDialogFragment.setRemoteFolderSummary(chosenFolder.getRemotePath()); } if (requestCode == SyncedFolderPreferencesDialogFragment.REQUEST_CODE__SELECT_LOCAL_FOLDER && resultCode == RESULT_OK && mSyncedFolderPreferencesDialogFragment != null) { String localPath = data.getStringExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES); mSyncedFolderPreferencesDialogFragment.setLocalFolderSummary(localPath); } else { super.onActivityResult(requestCode, resultCode, data); } } @Override public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext(). getContentResolver()); // custom folders newly created aren't in the list already, // so triggering a refresh if (MediaFolderType.CUSTOM == syncedFolder.getType() && syncedFolder.getId() == UNPERSISTED_ID) { SyncedFolderDisplayItem newCustomFolder = new SyncedFolderDisplayItem( SyncedFolder.UNPERSISTED_ID, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder.getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.getEnabled(), new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType()); long storedId = mSyncedFolderProvider.storeSyncedFolder(newCustomFolder); if (storedId != -1) { newCustomFolder.setId(storedId); if (newCustomFolder.isEnabled()) { FilesSyncHelper.insertAllDBEntriesForSyncedFolder(newCustomFolder); } else { String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + newCustomFolder.getId(); arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey); } } mAdapter.addSyncFolderItem(newCustomFolder); } else { SyncedFolderDisplayItem item = mAdapter.get(syncedFolder.getSection()); item = updateSyncedFolderItem(item, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder .getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder .getUploadAction(), syncedFolder.getEnabled()); if (syncedFolder.getId() == UNPERSISTED_ID) { // newly set up folder sync config long storedId = mSyncedFolderProvider.storeSyncedFolder(item); if (storedId != -1) { item.setId(storedId); if (item.isEnabled()) { FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item); } else { String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId(); arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey); } } } else { // existing synced folder setup to be updated mSyncedFolderProvider.updateSyncFolder(item); if (item.isEnabled()) { FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item); } else { String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId(); arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey); } } mAdapter.setSyncFolderItem(syncedFolder.getSection(), item); } mSyncedFolderPreferencesDialogFragment = null; if (syncedFolder.getEnabled()) { showBatteryOptimizationInfo(); } } @Override public void onCancelSyncedFolderPreference() { mSyncedFolderPreferencesDialogFragment = null; } @Override public void onDeleteSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { mSyncedFolderProvider.deleteSyncedFolder(syncedFolder.getId()); mAdapter.removeItem(syncedFolder.getSection()); } /** * update given synced folder with the given values. * * @param item the synced folder to be updated * @param localPath the local path * @param remotePath the remote path * @param wifiOnly upload on wifi only * @param chargingOnly upload on charging only * @param subfolderByDate created sub folders * @param uploadAction upload action * @param enabled is sync enabled * @return the updated item */ private SyncedFolderDisplayItem updateSyncedFolderItem(SyncedFolderDisplayItem item, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, Boolean subfolderByDate, Integer uploadAction, Boolean enabled) { item.setLocalPath(localPath); item.setRemotePath(remotePath); item.setWifiOnly(wifiOnly); item.setChargingOnly(chargingOnly); item.setSubfolderByDate(subfolderByDate); item.setUploadAction(uploadAction); item.setEnabled(enabled); return item; } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { switch (requestCode) { case PermissionUtil.PERMISSIONS_WRITE_EXTERNAL_STORAGE: { // If request is cancelled, result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted int gridWidth = getResources().getInteger(R.integer.media_grid_width); load(gridWidth * 2, true); } else { // permission denied --> do nothing return; } return; } default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } @Override protected void onResume() { super.onResume(); setDrawerMenuItemChecked(R.id.nav_synced_folders); } private void showBatteryOptimizationInfo() { if (powerManagementService.isPowerSavingExclusionAvailable() || checkIfBatteryOptimizationEnabled()) { AlertDialog alertDialog = new AlertDialog.Builder(this, R.style.Theme_ownCloud_Dialog) .setTitle(getString(R.string.battery_optimization_title)) .setMessage(getString(R.string.battery_optimization_message)) .setPositiveButton(getString(R.string.battery_optimization_disable), (dialog, which) -> { // show instant upload if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @SuppressLint("BatteryLife") Intent intent = new Intent(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse("package:" + BuildConfig.APPLICATION_ID)); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); } } else { Intent powerUsageIntent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY); if (getPackageManager().resolveActivity(powerUsageIntent, 0) != null) { startActivity(powerUsageIntent); } else { dialog.dismiss(); DisplayUtils.showSnackMessage(this, getString(R.string.battery_optimization_no_setting)); } } }) .setNegativeButton(getString(R.string.battery_optimization_close), (dialog, which) -> dialog.dismiss()) .setIcon(R.drawable.ic_battery_alert) .show(); int color = ThemeUtils.primaryAccentColor(this); alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(color); alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(color); } } /** * Check if battery optimization is enabled. If unknown, fallback to true. * * @return true if battery optimization is enabled */ private boolean checkIfBatteryOptimizationEnabled() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); if (powerManager == null) { return true; } return !powerManager.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID); } else { return true; } } }