MediaProvider: MediaScanner behavior on boot (2/3)

Lets you enable/disable/ask (with a notification) media scanning on boot.
Can speed up boot and reduce initial boot lag from MediaScanner

- added xxhdpi icon
- no need for multiuser due that this should be a one time set
  general option set by the owner of the device

Change-Id: Iad0b72f8b653b3d567b27de3ccf5c6cfb07e40ed
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7d48a4a..1fc63be 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -48,6 +48,10 @@
 
         <receiver android:name="MediaScannerReceiver">
             <intent-filter>
+                <action android:name="com.android.providers.media.SCAN_ALL" />
+                <action android:name="com.android.providers.media.DISMISS_SCAN" />
+            </intent-filter>
+            <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
             </intent-filter>
             <intent-filter>
diff --git a/res/drawable-hdpi/ask_scan.png b/res/drawable-hdpi/ask_scan.png
new file mode 100644
index 0000000..a1d9855
--- /dev/null
+++ b/res/drawable-hdpi/ask_scan.png
Binary files differ
diff --git a/res/drawable-mdpi/ask_scan.png b/res/drawable-mdpi/ask_scan.png
new file mode 100644
index 0000000..864926f
--- /dev/null
+++ b/res/drawable-mdpi/ask_scan.png
Binary files differ
diff --git a/res/drawable-xhdpi/ask_scan.png b/res/drawable-xhdpi/ask_scan.png
new file mode 100644
index 0000000..0a67738
--- /dev/null
+++ b/res/drawable-xhdpi/ask_scan.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ask_scan.png b/res/drawable-xxhdpi/ask_scan.png
new file mode 100644
index 0000000..957c2f6
--- /dev/null
+++ b/res/drawable-xxhdpi/ask_scan.png
Binary files differ
diff --git a/res/values/slim_strings.xml b/res/values/slim_strings.xml
new file mode 100644
index 0000000..37fbb26
--- /dev/null
+++ b/res/values/slim_strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 SlimRoms Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Ask to scan on boot -->
+    <string name="ask_scan_title">Scan for new media?</string>
+    <string name="ask_scan_text">Click to manually trigger media scan</string>
+
+</resources>
diff --git a/src/com/android/providers/media/MediaScannerReceiver.java b/src/com/android/providers/media/MediaScannerReceiver.java
index 89635b9..65dc83a 100644
--- a/src/com/android/providers/media/MediaScannerReceiver.java
+++ b/src/com/android/providers/media/MediaScannerReceiver.java
@@ -17,58 +17,151 @@
 
 package com.android.providers.media;
 
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.Handler;
+import android.provider.Settings;
 import android.util.Log;
 
 import java.io.File;
 import java.io.IOException;
 
 public class MediaScannerReceiver extends BroadcastReceiver {
+    private final static int BOOT_SCAN_ENABLE = 0;
+    private final static int BOOT_SCAN_ASK = 1;
+    private final static int BOOT_SCAN_DISABLE = 2;
+
+    private final static int NOTIFICATION_ID = 1;
+
+    // This delay prevents a scan on boot from mounting the sdcard
+    private final static int DELAY_MS = 120000; // 2m
+
     private final static String TAG = "MediaScannerReceiver";
 
+    private final static String SCAN_ALL = "com.android.providers.media.SCAN_ALL";
+    private final static String DISMISS_SCAN = "com.android.providers.media.DISMISS_SCAN";
+
+    private Handler mDelayScan = new Handler();
+
     @Override
     public void onReceive(Context context, Intent intent) {
         final String action = intent.getAction();
         final Uri uri = intent.getData();
+
+        // MediaScanner behavior on boot
+        final int msob = Settings.System.getInt(context.getContentResolver(),
+                Settings.System.MEDIA_SCANNER_ON_BOOT, 0);
+
         if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-            // Scan both internal and external storage
+            switch (msob) {
+                case BOOT_SCAN_ENABLE:
+                    // Scan both internal and external storage
+
+                    scan(context, MediaProvider.INTERNAL_VOLUME);
+                    scan(context, MediaProvider.EXTERNAL_VOLUME);
+                    break;
+                case BOOT_SCAN_ASK:
+                    askToScan(context, true);
+                    break;
+                case BOOT_SCAN_DISABLE:
+                    askToScan(context, false);
+                    removeAsk(context, DELAY_MS);
+                    break;
+            }
+        } else if (SCAN_ALL.equals(action)) {
+            removeAsk(context, DELAY_MS);
+
             scan(context, MediaProvider.INTERNAL_VOLUME);
             scan(context, MediaProvider.EXTERNAL_VOLUME);
-
+        } else if (DISMISS_SCAN.equals(action)) {
+            removeAsk(context, DELAY_MS);
         } else {
             if (uri.getScheme().equals("file")) {
-                // handle intents related to external storage
-                String path = uri.getPath();
-                String externalStoragePath = Environment.getExternalStorageDirectory().getPath();
-                String legacyPath = Environment.getLegacyExternalStorageDirectory().getPath();
+                if (!checkAsk(context)) {
+                    // handle intents related to external storage
+                    String path = uri.getPath();
+                    String externalStoragePath = Environment.getExternalStorageDirectory().getPath();
+                    String legacyPath = Environment.getLegacyExternalStorageDirectory().getPath();
 
-                try {
-                    path = new File(path).getCanonicalPath();
-                } catch (IOException e) {
-                    Log.e(TAG, "couldn't canonicalize " + path);
-                    return;
-                }
-                if (path.startsWith(legacyPath)) {
-                    path = externalStoragePath + path.substring(legacyPath.length());
-                }
+                    try {
+                        path = new File(path).getCanonicalPath();
+                    } catch (IOException e) {
+                        Log.e(TAG, "couldn't canonicalize " + path);
+                        return;
+                    }
+                    if (path.startsWith(legacyPath)) {
+                        path = externalStoragePath + path.substring(legacyPath.length());
+                    }
 
-                Log.d(TAG, "action: " + action + " path: " + path);
-                if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
-                    // scan whenever any volume is mounted
-                    scan(context, MediaProvider.EXTERNAL_VOLUME);
-                } else if (Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action) &&
-                        path != null && path.startsWith(externalStoragePath + "/")) {
-                    scanFile(context, path);
+                    Log.d(TAG, "action: " + action + " path: " + path);
+                    if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
+                        // scan whenever any volume is mounted
+                        scan(context, MediaProvider.EXTERNAL_VOLUME);
+                    } else if (Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action) &&
+                            path != null && path.startsWith(externalStoragePath + "/")) {
+                        scanFile(context, path);
+                    }
                 }
             }
         }
     }
 
+    private boolean checkAsk(Context context) {
+        final Intent intent = new Intent(SCAN_ALL);
+        boolean askExists = (PendingIntent.getBroadcast(context,
+                1, intent, PendingIntent.FLAG_NO_CREATE) != null);
+        return askExists;
+    }
+
+    private void removeAsk(final Context context, final int delay) {
+        mDelayScan.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                final Intent intent = new Intent(SCAN_ALL);
+                PendingIntent.getBroadcast(context, 1, intent,
+                        PendingIntent.FLAG_UPDATE_CURRENT).cancel();
+            }
+        }, delay);
+    }
+
+    public void askToScan(Context context, boolean startNotify) {
+
+        Intent intent = new Intent(SCAN_ALL);
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
+                1, intent, 0);
+
+        if (!startNotify) {
+            return;
+        }
+
+        Notification.Builder mBuilder = new Notification.Builder(context)
+            .setContentTitle(context.getString(R.string.ask_scan_title))
+            .setContentText(context.getString(R.string.ask_scan_text))
+            .setSmallIcon(R.drawable.ask_scan);
+
+        mBuilder.setContentIntent(pendingIntent);
+
+        intent = new Intent(DISMISS_SCAN);
+        pendingIntent = PendingIntent.getBroadcast(context,
+                2, intent, 0);
+        mBuilder.setDeleteIntent(pendingIntent);
+
+        NotificationManager notificationManager =
+            (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        Notification notif = mBuilder.build();
+        notif.flags    |= Notification.FLAG_AUTO_CANCEL;
+        notif.priority  = Notification.PRIORITY_HIGH;
+
+        notificationManager.notify(NOTIFICATION_ID, notif);
+    }
+
     private void scan(Context context, String volume) {
         Bundle args = new Bundle();
         args.putString("volume", volume);