Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

request media_location #14247

Merged
merged 11 commits into from
Jan 14, 2025
2 changes: 2 additions & 0 deletions app/src/gplay/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
tools:node="remove"
tools:ignore="ScopedStorage" />

<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
tobiasKaminsky marked this conversation as resolved.
Show resolved Hide resolved

<application
android:name=".MainApp"
android:icon="@mipmap/ic_launcher"
Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<uses-permission
android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
android:maxSdkVersion="25" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
<!--
Apps that target Android 9 (API level 28) or higher and use foreground services
must request the FOREGROUND_SERVICE permission
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.nextcloud.client.core.Clock
import com.nextcloud.client.preferences.AppPreferences
import com.nextcloud.client.preferences.AppPreferencesImpl
import com.nextcloud.utils.BuildHelper
import com.owncloud.android.MainApp
import com.owncloud.android.R
import com.owncloud.android.datamodel.ArbitraryDataProvider
Expand All @@ -41,6 +42,7 @@
import com.owncloud.android.datamodel.MediaProvider
import com.owncloud.android.datamodel.SyncedFolderProvider
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.ui.activity.FileDisplayActivity
import com.owncloud.android.ui.activity.ManageAccountsActivity
import com.owncloud.android.ui.activity.SyncedFoldersActivity
import com.owncloud.android.ui.notifications.NotificationUtils
Expand Down Expand Up @@ -190,9 +192,51 @@
gson.toJson(mediaFoldersModel)
)
}

// only send notification when synced folder is setup, gplay flavor and not branded client
@Suppress("ComplexMethod")
if (syncedFolderProvider.syncedFolders.isNotEmpty() &&
BuildHelper.isFlavourGPlay() &&
!preferences.isAutoUploadGPlayNotificationShown &&
!MainApp.isClientBranded()
) {
sendAutoUploadNotification()
tobiasKaminsky marked this conversation as resolved.
Show resolved Hide resolved
}

return Result.success()
}

private fun sendAutoUploadNotification() {
val notificationId = randomIdGenerator.nextInt()
val intent = Intent().apply {
setClassName(context, FileDisplayActivity::class.java.name)
setAction(FileDisplayActivity.AUTO_UPLOAD_NOTIFICATION)
}
val pendingIntent = PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)

val notificationBuilder = NotificationCompat.Builder(
context,
NotificationUtils.NOTIFICATION_CHANNEL_GENERAL
)
.setSmallIcon(R.drawable.notification_icon)
.setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.notification_icon))
.setContentTitle(context.getString(R.string.re_enable_auto_upload))
.setContentText(context.getString(R.string.click_to_learn_how_to_re_enable_auto_uploads))
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setAutoCancel(true)
.setContentIntent(pendingIntent)

viewThemeUtils.androidx.themeNotificationCompatBuilder(context, notificationBuilder)

val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(notificationId, notificationBuilder.build())

Check failure

Code scanning / CodeQL

Use of implicit PendingIntents High

An implicit Intent is created
and sent to an unspecified third party through a PendingIntent.
This autofix suggestion was applied.
Show autofix suggestion Hide autofix suggestion

Copilot Autofix AI 6 days ago

To fix the problem, we need to ensure that the Intent used to create the PendingIntent is explicit. This can be done by setting the component name explicitly in the Intent. Additionally, we should verify that all other PendingIntent creations in the code follow the same practice.

  • Modify the Intent on line 210 to explicitly set the component name.
  • Ensure that the PendingIntent is created with the FLAG_IMMUTABLE flag to prevent modification by other applications.
Suggested changeset 1
app/src/main/java/com/nextcloud/client/jobs/MediaFoldersDetectionWork.kt

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/app/src/main/java/com/nextcloud/client/jobs/MediaFoldersDetectionWork.kt b/app/src/main/java/com/nextcloud/client/jobs/MediaFoldersDetectionWork.kt
--- a/app/src/main/java/com/nextcloud/client/jobs/MediaFoldersDetectionWork.kt
+++ b/app/src/main/java/com/nextcloud/client/jobs/MediaFoldersDetectionWork.kt
@@ -209,3 +209,4 @@
         val notificationId = randomIdGenerator.nextInt()
-        val intent = Intent(context, FileDisplayActivity::class.java).apply {
+        val intent = Intent().apply {
+            setClassName(context, FileDisplayActivity::class.java.name)
             setAction(FileDisplayActivity.AUTO_UPLOAD_NOTIFICATION)
EOF
@@ -209,3 +209,4 @@
val notificationId = randomIdGenerator.nextInt()
val intent = Intent(context, FileDisplayActivity::class.java).apply {
val intent = Intent().apply {
setClassName(context, FileDisplayActivity::class.java.name)
setAction(FileDisplayActivity.AUTO_UPLOAD_NOTIFICATION)
Copilot is powered by AI and may make mistakes. Always verify output.
@tobiasKaminsky tobiasKaminsky committed this autofix suggestion 6 days ago.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
}

@Suppress("LongMethod")
private fun sendNotification(contentTitle: String, subtitle: String, user: User, path: String, type: Int) {
val notificationId = randomIdGenerator.nextInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,4 +403,10 @@ default void onDarkThemeModeChanged(DarkMode mode) {

boolean isAutoUploadGPlayWarningShown();
void setAutoUploadGPlayWarningShown(boolean value);

boolean isAutoUploadGPlayWarning2Shown();
void setAutoUploadGPlayWarning2Shown(boolean value);

boolean isAutoUploadGPlayNotificationShown();
void setAutoUploadGPlayNotificationShown(boolean value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public final class AppPreferencesImpl implements AppPreferences {
private static final String PREF__STOP_DOWNLOAD_JOBS_ON_START = "stop_download_jobs_on_start";

private static final String PREF__AUTO_UPLOAD_GPLAY_WARNING_SHOWN = "auto_upload_gplay_warning_shown";
private static final String PREF__AUTO_UPLOAD_GPLAY_WARNING2_SHOWN = "auto_upload_gplay_warning2_shown";
private static final String PREF__AUTO_UPLOAD_GPLAY_NOTIFICATION_SHOWN = "auto_upload_gplay_notification_shown";

private static final String LOG_ENTRY = "log_entry";

Expand Down Expand Up @@ -837,4 +839,24 @@ public boolean isAutoUploadGPlayWarningShown() {
public void setAutoUploadGPlayWarningShown(boolean value) {
preferences.edit().putBoolean(PREF__AUTO_UPLOAD_GPLAY_WARNING_SHOWN, value).apply();
}

@Override
public boolean isAutoUploadGPlayWarning2Shown() {
return preferences.getBoolean(PREF__AUTO_UPLOAD_GPLAY_WARNING2_SHOWN, false);
}

@Override
public void setAutoUploadGPlayWarning2Shown(boolean value) {
preferences.edit().putBoolean(PREF__AUTO_UPLOAD_GPLAY_WARNING2_SHOWN, value).apply();
}

@Override
public boolean isAutoUploadGPlayNotificationShown() {
return preferences.getBoolean(PREF__AUTO_UPLOAD_GPLAY_NOTIFICATION_SHOWN, false);
}

@Override
public void setAutoUploadGPlayNotificationShown(boolean value) {
preferences.edit().putBoolean(PREF__AUTO_UPLOAD_GPLAY_NOTIFICATION_SHOWN, value).apply();
}
}
6 changes: 5 additions & 1 deletion app/src/main/java/com/owncloud/android/MainApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -417,9 +417,13 @@ private void setProxyForNonBrandedPlusClients() {
Log_OC.d(TAG, "Error caught at setProxyForNonBrandedPlusClients: " + e);
}
}

public static boolean isClientBranded() {
return getAppContext().getResources().getBoolean(R.bool.is_branded_client);
}

public static boolean isClientBrandedPlus() {
return (getAppContext().getResources().getBoolean(R.bool.is_branded_plus_client));
return getAppContext().getResources().getBoolean(R.bool.is_branded_plus_client);
}

private final IntentFilter restrictionsFilter = new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/java/com/owncloud/android/datamodel/OCFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import android.text.TextUtils;

import com.nextcloud.utils.BuildHelper;
import com.owncloud.android.BuildConfig;
import com.owncloud.android.R;
import com.owncloud.android.lib.common.network.WebdavEntry;
import com.owncloud.android.lib.common.network.WebdavUtils;
Expand Down Expand Up @@ -1093,7 +1092,7 @@ public void setInternalFolderSyncResult(String internalFolderSyncResult) {
}

public boolean isAPKorAAB() {
if (BuildHelper.GPLAY.equals(BuildConfig.FLAVOR)) {
if (BuildHelper.INSTANCE.isFlavourGPlay()) {
return getFileName().endsWith(".apk") || getFileName().endsWith(".aab");
} else {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@
import com.nextcloud.utils.extensions.IntentExtensionsKt;
import com.nextcloud.utils.fileNameValidator.FileNameValidator;
import com.nextcloud.utils.view.FastScrollUtils;
import com.owncloud.android.BuildConfig;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.databinding.FilesBinding;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.MediaFolderType;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.SyncedFolder;
import com.owncloud.android.datamodel.SyncedFolderProvider;
Expand Down Expand Up @@ -178,6 +178,7 @@ public class FileDisplayActivity extends FileActivity
public static final String RESTART = "RESTART";
public static final String ALL_FILES = "ALL_FILES";
public static final String LIST_GROUPFOLDERS = "LIST_GROUPFOLDERS";
public static final String AUTO_UPLOAD_NOTIFICATION = "AUTO_UPLOAD_NOTIFICATION";
public static final int SINGLE_USER_SIZE = 1;
public static final String OPEN_FILE = "NC_OPEN_FILE";
public static final String FOLDER_SYNC_CONFLICT = "FOLDER_SYNC_CONFLICT";
Expand Down Expand Up @@ -282,7 +283,8 @@ protected void onCreate(Bundle savedInstanceState) {
mPlayerConnection = new PlayerServiceConnection(this);

checkStoragePath();
checkAutoUploadOnGPlay();
notifyGPlayPermissionChanges();
showAutoUploadWarningForGPlayFlavour();

initSyncBroadcastReceiver();
observeWorkerState();
Expand All @@ -292,8 +294,8 @@ protected void onCreate(Bundle savedInstanceState) {
offlineFolderConflictManager.registerRefreshSearchEventReceiver();
}

private void checkAutoUploadOnGPlay() {
if (!BuildHelper.GPLAY.equals(BuildConfig.FLAVOR)) {
private void notifyGPlayPermissionChanges() {
if (!BuildHelper.INSTANCE.isFlavourGPlay() || MainApp.isClientBranded()) {
return;
}

Expand Down Expand Up @@ -335,6 +337,41 @@ private void checkAutoUploadOnGPlay() {
preferences.setAutoUploadGPlayWarningShown(true);
}

private void showAutoUploadWarningForGPlayFlavour() {
if (!BuildHelper.INSTANCE.isFlavourGPlay() || MainApp.isClientBranded()) {
return;
}

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
return;
}

boolean showAutoUploadDialog = false;
for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
if (syncedFolder.getType() == MediaFolderType.CUSTOM) {
showAutoUploadDialog = true;
break;
}
}

if (!preferences.isAutoUploadGPlayWarning2Shown()) {
String title = showAutoUploadDialog ? getString(R.string.auto_upload_gplay) : getString(R.string.upload_gplay);
String message = showAutoUploadDialog ? getString(R.string.auto_upload_gplay_desc2) : getString(R.string.upload_gplay_desc);

new MaterialAlertDialogBuilder(this, R.style.Theme_ownCloud_Dialog)
.setTitle(title)
.setMessage(message)
.setNegativeButton(R.string.dialog_close, (dialog, which) -> {
PermissionUtil.requestMediaLocationPermission(this);
preferences.setAutoUploadGPlayWarning2Shown(true);
dialog.dismiss();
})
.setIcon(R.drawable.nav_synced_folders)
.create()
.show();
}
}

@SuppressWarnings("unchecked")
private void loadSavedInstanceState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
Expand Down Expand Up @@ -635,9 +672,24 @@ protected void onNewIntent(Intent intent) {
DrawerActivity.menuItemId = R.id.nav_groupfolders;
setLeftFragment(new GroupfolderListFragment());
getSupportFragmentManager().executePendingTransactions();
} else if (AUTO_UPLOAD_NOTIFICATION.equals(intent.getAction())) {
handleAutoUploadNotification();
}
}
}

private void handleAutoUploadNotification() {
new MaterialAlertDialogBuilder(this, R.style.Theme_ownCloud_Dialog)
.setTitle(R.string.re_enable_auto_upload)
.setMessage(R.string.re_enable_auto_upload_desc)
.setNegativeButton(R.string.dialog_close, (dialog, which) -> {
dialog.dismiss();
preferences.setAutoUploadGPlayNotificationShown(true);
})
.setIcon(R.drawable.nav_synced_folders)
.create()
.show();
}

private void onOpenFileIntent(Intent intent) {
String extra = intent.getStringExtra(EXTRA_FILE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ public void registerFabListener() {
// is not available in FolderPickerActivity
viewThemeUtils.material.themeFAB(mFabMain);
mFabMain.setOnClickListener(v -> {
PermissionUtil.requestMediaLocationPermission(activity);

tobiasKaminsky marked this conversation as resolved.
Show resolved Hide resolved
final OCFileListBottomSheetDialog dialog =
new OCFileListBottomSheetDialog(activity,
this,
Expand Down
23 changes: 22 additions & 1 deletion app/src/main/java/com/owncloud/android/utils/PermissionUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ object PermissionUtil {
const val PERMISSIONS_READ_CALENDAR_AUTOMATIC = 6
const val PERMISSIONS_WRITE_CALENDAR = 7
const val PERMISSIONS_POST_NOTIFICATIONS = 8
const val PERMISSIONS_MEDIA_LOCATION = 9

const val REQUEST_CODE_MANAGE_ALL_FILES = 19203

Expand Down Expand Up @@ -151,7 +152,8 @@ object PermissionUtil {
// use granular media permissions
arrayOf(
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_VIDEO
Manifest.permission.READ_MEDIA_VIDEO,
Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
)
} else {
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
Expand Down Expand Up @@ -302,4 +304,23 @@ object PermissionUtil {
}
}
}

/**
* Request media location permission. Required on API level >= 34.
* Does not have any effect on API level < 34.
*
* @param activity target activity
*/
@JvmStatic
fun requestMediaLocationPermission(activity: Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (!checkSelfPermission(activity, Manifest.permission.ACCESS_MEDIA_LOCATION)) {
ActivityCompat.requestPermissions(
activity,
arrayOf(Manifest.permission.ACCESS_MEDIA_LOCATION),
PERMISSIONS_MEDIA_LOCATION
)
}
}
}
}
6 changes: 6 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1297,4 +1297,10 @@
<string name="auto_upload_gplay">Auto upload behaviour changed</string>
<string name="auto_upload_gplay_desc">Due to new restrictions imposed by Google, the auto upload feature will no longer be able to automatically remove uploaded files.</string>
<string name="pick_contact_to_share_with">Pick contact to share with</string>
<string name="auto_upload_gplay_desc2">Due to new restrictions imposed by Google, we have been forced to remove an important permission. As a result, auto upload will only be able to upload image and video files.\nAdditionally, a new permission is required to access location information for uploaded images.\nMake sure that media access is set to \"Always allow all\".</string>
<string name="upload_gplay">Permission changes</string>
<string name="upload_gplay_desc">A new permission is required to access location information for uploaded images.\nMake sure that media access is set to \"Always allow all\".</string>
<string name="re_enable_auto_upload">Changes to auto upload</string>
<string name="re_enable_auto_upload_desc">Due to new restrictions imposed by Google, we have been forced to remove an important permission. We are currently working with Google to resolve this issue and restore full functionality.\n\nTo re-enable auto upload for new photos and videos:\nSelect \"Allow all\" in the following dialogue or the system settings.\nAllow Nextcloud to use location when prompted, as this allows us to store location when uploading images.\n\nAuto upload will no longer be able to upload any other files when using the Google Play version of the Nextcloud app.\n\nPlease check for any files that may not have been uploaded since December 2024.</string>
<string name="click_to_learn_how_to_re_enable_auto_uploads">Manual intervention required to re-enable auto-upload</string>
</resources>
2 changes: 1 addition & 1 deletion scripts/analysis/lint-results.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
DO NOT TOUCH; GENERATED BY DRONE
<span class="mdl-layout-title">Lint Report: 3 errors and 56 warnings</span>
<span class="mdl-layout-title">Lint Report: 3 errors and 55 warnings</span>
Loading