Fix notification bugs, cleanup DownloadService + DownloadReceiver
[android/platform/packages/providers/DownloadProvider.git] / src / com / android / providers / downloads / DownloadReceiver.java
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.providers.downloads;
18
19 import android.content.ActivityNotFoundException;
20 import android.content.BroadcastReceiver;
21 import android.content.ContentUris;
22 import android.content.ContentValues;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.database.Cursor;
26 import android.net.ConnectivityManager;
27 import android.net.DownloadManager;
28 import android.net.NetworkInfo;
29 import android.net.Uri;
30 import android.provider.Downloads;
31 import android.util.Log;
32
33 import com.google.common.annotations.VisibleForTesting;
34
35 import java.io.File;
36
37 /**
38  * Receives system broadcasts (boot, network connectivity)
39  */
40 public class DownloadReceiver extends BroadcastReceiver {
41     @VisibleForTesting
42     SystemFacade mSystemFacade = null;
43
44     public void onReceive(Context context, Intent intent) {
45         if (mSystemFacade == null) {
46             mSystemFacade = new RealSystemFacade(context);
47         }
48
49         String action = intent.getAction();
50         if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
51             startService(context);
52         } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
53             NetworkInfo info = (NetworkInfo)
54                     intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
55             if (info != null && info.isConnected()) {
56                 startService(context);
57             }
58         } else if (action.equals(Constants.ACTION_RETRY)) {
59             startService(context);
60         } else if (action.equals(Constants.ACTION_OPEN)
61                 || action.equals(Constants.ACTION_LIST)
62                 || action.equals(Constants.ACTION_HIDE)) {
63             handleNotificationBroadcast(context, intent);
64         }
65     }
66
67     /**
68      * Handle any broadcast related to a system notification.
69      */
70     private void handleNotificationBroadcast(Context context, Intent intent) {
71         Uri uri = intent.getData();
72         String action = intent.getAction();
73         if (Constants.LOGVV) {
74             if (action.equals(Constants.ACTION_OPEN)) {
75                 Log.v(Constants.TAG, "Receiver open for " + uri);
76             } else if (action.equals(Constants.ACTION_LIST)) {
77                 Log.v(Constants.TAG, "Receiver list for " + uri);
78             } else { // ACTION_HIDE
79                 Log.v(Constants.TAG, "Receiver hide for " + uri);
80             }
81         }
82
83         Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
84         if (cursor == null) {
85             return;
86         }
87         try {
88             if (!cursor.moveToFirst()) {
89                 return;
90             }
91
92             if (action.equals(Constants.ACTION_OPEN)) {
93                 openDownload(context, cursor);
94                 hideNotification(context, uri, cursor);
95             } else if (action.equals(Constants.ACTION_LIST)) {
96                 sendNotificationClickedIntent(intent, cursor);
97             } else { // ACTION_HIDE
98                 hideNotification(context, uri, cursor);
99             }
100         } finally {
101             cursor.close();
102         }
103     }
104
105     /**
106      * Hide a system notification for a download.
107      * @param uri URI to update the download
108      * @param cursor Cursor for reading the download's fields
109      */
110     private void hideNotification(Context context, Uri uri, Cursor cursor) {
111         mSystemFacade.cancelNotification(ContentUris.parseId(uri));
112
113         int statusColumn = cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_STATUS);
114         int status = cursor.getInt(statusColumn);
115         int visibilityColumn =
116                 cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_VISIBILITY);
117         int visibility = cursor.getInt(visibilityColumn);
118         if (Downloads.Impl.isStatusCompleted(status)
119                 && visibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) {
120             ContentValues values = new ContentValues();
121             values.put(Downloads.Impl.COLUMN_VISIBILITY,
122                     Downloads.Impl.VISIBILITY_VISIBLE);
123             context.getContentResolver().update(uri, values, null, null);
124         }
125     }
126
127     /**
128      * Open the download that cursor is currently pointing to, since it's completed notification
129      * has been clicked.
130      */
131     private void openDownload(Context context, Cursor cursor) {
132         String filename = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl._DATA));
133         String mimetype =
134             cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_MIME_TYPE));
135         Uri path = Uri.parse(filename);
136         // If there is no scheme, then it must be a file
137         if (path.getScheme() == null) {
138             path = Uri.fromFile(new File(filename));
139         }
140
141         Intent activityIntent = new Intent(Intent.ACTION_VIEW);
142         activityIntent.setDataAndType(path, mimetype);
143         activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
144         try {
145             context.startActivity(activityIntent);
146         } catch (ActivityNotFoundException ex) {
147             Log.d(Constants.TAG, "no activity for " + mimetype, ex);
148         }
149     }
150
151     /**
152      * Notify the owner of a running download that its notification was clicked.
153      * @param intent the broadcast intent sent by the notification manager
154      * @param cursor Cursor for reading the download's fields
155      */
156     private void sendNotificationClickedIntent(Intent intent, Cursor cursor) {
157         String pckg = cursor.getString(
158                 cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE));
159         if (pckg == null) {
160             return;
161         }
162
163         String clazz = cursor.getString(
164                 cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_NOTIFICATION_CLASS));
165         boolean isPublicApi =
166                 cursor.getInt(cursor.getColumnIndex(Downloads.Impl.COLUMN_IS_PUBLIC_API)) != 0;
167
168         Intent appIntent = null;
169         if (isPublicApi) {
170             appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED);
171             appIntent.setPackage(pckg);
172         } else { // legacy behavior
173             if (clazz == null) {
174                 return;
175             }
176             appIntent = new Intent(Downloads.Impl.ACTION_NOTIFICATION_CLICKED);
177             appIntent.setClassName(pckg, clazz);
178             if (intent.getBooleanExtra("multiple", true)) {
179                 appIntent.setData(Downloads.Impl.CONTENT_URI);
180             } else {
181                 long downloadId = cursor.getLong(cursor.getColumnIndexOrThrow(Downloads.Impl._ID));
182                 appIntent.setData(
183                         ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, downloadId));
184             }
185         }
186
187         mSystemFacade.sendBroadcast(appIntent);
188     }
189
190     private void startService(Context context) {
191         context.startService(new Intent(context, DownloadService.class));
192     }
193 }