13b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen/*
23b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen * Copyright (C) 2012 The Android Open Source Project
33b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen *
43b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen * Licensed under the Apache License, Version 2.0 (the "License");
53b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen * you may not use this file except in compliance with the License.
63b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen * You may obtain a copy of the License at
73b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen *
83b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen *      http://www.apache.org/licenses/LICENSE-2.0
93b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen *
103b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen * Unless required by applicable law or agreed to in writing, software
113b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen * distributed under the License is distributed on an "AS IS" BASIS,
123b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen * See the License for the specific language governing permissions and
143b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen * limitations under the License.
153b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen */
163b5a46f86f97d1624d82fa4c56ef175308e7dc65Martijn Coenen
17b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralespackage com.android.nfc.beam;
18b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
19b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport com.android.nfc.R;
20be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
21be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.app.Notification;
223cdb596c8e3a19515545eb060696fa2a335b9db0Ruchi Kandoiimport android.app.NotificationChannel;
23be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.app.NotificationManager;
24be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.app.PendingIntent;
25be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.app.Notification.Builder;
26be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.bluetooth.BluetoothDevice;
27be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.content.ContentResolver;
28be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.content.Context;
29be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.content.Intent;
30be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.media.MediaScannerConnection;
31be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.net.Uri;
32be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.os.Environment;
33be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.os.Handler;
34be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.os.Looper;
35be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.os.Message;
36be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.os.SystemClock;
37be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.os.UserHandle;
38be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport android.util.Log;
39be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
40be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport java.io.File;
41be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport java.text.SimpleDateFormat;
42be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport java.util.ArrayList;
43b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport java.util.Arrays;
44be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport java.util.Date;
45be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Projectimport java.util.HashMap;
46116dfa0107cdc7ab098ed95828280542bf6b0a1eThe Android Open Source Projectimport java.util.Locale;
47be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
48d5567fc2ff2d4e462932b40450f536b7e1a3bf3bJohan Redestigimport android.support.v4.content.FileProvider;
49d5567fc2ff2d4e462932b40450f536b7e1a3bf3bJohan Redestig
50be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project/**
51b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales * A BeamTransferManager object represents a set of files
52be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project * that were received through NFC connection handover
53be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project * from the same source address.
54be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project *
55b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales * It manages starting, stopping, and processing the transfer, as well
56b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales * as the user visible notification.
57b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales *
58be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project * For Bluetooth, files are received through OPP, and
59be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project * we have no knowledge how many files will be transferred
60be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project * as part of a single transaction.
61be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project * Hence, a transfer has a notion of being "alive": if
62be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project * the last update to a transfer was within WAIT_FOR_NEXT_TRANSFER_MS
63be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project * milliseconds, we consider a new file transfer from the
64be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project * same source address as part of the same transfer.
65be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project * The corresponding URIs will be grouped in a single folder.
66be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project *
67b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales * @hide
68be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project */
69be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
70b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralespublic class BeamTransferManager implements Handler.Callback,
71b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        MediaScannerConnection.OnScanCompletedListener {
72be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    interface Callback {
733ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales
74b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        void onTransferComplete(BeamTransferManager transfer, boolean success);
75be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    };
76b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    static final String TAG = "BeamTransferManager";
77be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
78be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final Boolean DBG = true;
79be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
80be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    // In the states below we still accept new file transfer
81be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final int STATE_NEW = 0;
82be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final int STATE_IN_PROGRESS = 1;
83be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final int STATE_W4_NEXT_TRANSFER = 2;
84be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    // In the states below no new files are accepted.
85be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final int STATE_W4_MEDIA_SCANNER = 3;
86be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final int STATE_FAILED = 4;
87be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final int STATE_SUCCESS = 5;
88be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final int STATE_CANCELLED = 6;
893ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales    static final int STATE_CANCELLING = 7;
90be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final int MSG_NEXT_TRANSFER_TIMER = 0;
91fbd59bd47482eb0784077ee3faf498d531497304Andres Morales
92be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final int MSG_TRANSFER_TIMEOUT = 1;
93b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    static final int DATA_LINK_TYPE_BLUETOOTH = 1;
94be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
95be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    // We need to receive an update within this time period
96be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    // to still consider this transfer to be "alive" (ie
97be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    // a reason to keep the handover transport enabled).
98be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final int ALIVE_CHECK_MS = 20000;
99be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
100be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    // The amount of time to wait for a new transfer
101be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    // once the current one completes.
102be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final int WAIT_FOR_NEXT_TRANSFER_MS = 4000;
103be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
104be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    static final String BEAM_DIR = "beam";
105be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
1063cdb596c8e3a19515545eb060696fa2a335b9db0Ruchi Kandoi    static final String BEAM_NOTIFICATION_CHANNEL = "beam_notification_channel";
1073cdb596c8e3a19515545eb060696fa2a335b9db0Ruchi Kandoi
108b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    static final String ACTION_WHITELIST_DEVICE =
109b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales            "android.btopp.intent.action.WHITELIST_DEVICE";
110b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
111b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    static final String ACTION_STOP_BLUETOOTH_TRANSFER =
112b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales            "android.btopp.intent.action.STOP_HANDOVER_TRANSFER";
113b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
114be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    final boolean mIncoming;  // whether this is an incoming transfer
115fbd59bd47482eb0784077ee3faf498d531497304Andres Morales
116be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    final int mTransferId; // Unique ID of this transfer used for notifications
1173ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales    int mBluetoothTransferId; // ID of this transfer in Bluetooth namespace
118fbd59bd47482eb0784077ee3faf498d531497304Andres Morales
119be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    final PendingIntent mCancelIntent;
120be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    final Context mContext;
121be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    final Handler mHandler;
122be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    final NotificationManager mNotificationManager;
123be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    final BluetoothDevice mRemoteDevice;
124be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    final Callback mCallback;
125b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    final boolean mRemoteActivating;
126be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
127be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    // Variables below are only accessed on the main thread
128be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    int mState;
129d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen    int mCurrentCount;
130d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen    int mSuccessCount;
131d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen    int mTotalCount;
132b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    int mDataLinkType;
133be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    boolean mCalledBack;
134be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    Long mLastUpdate; // Last time an event occurred for this transfer
135be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    float mProgress; // Progress in range [0..1]
136fbd59bd47482eb0784077ee3faf498d531497304Andres Morales    ArrayList<Uri> mUris; // Received uris from transport
137fbd59bd47482eb0784077ee3faf498d531497304Andres Morales    ArrayList<String> mTransferMimeTypes; // Mime-types received from transport
138b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    Uri[] mOutgoingUris; // URIs to send
139be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    ArrayList<String> mPaths; // Raw paths on the filesystem for Beam-stored files
140be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    HashMap<String, String> mMimeTypes; // Mime-types associated with each path
141be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    HashMap<String, Uri> mMediaUris; // URIs found by the media scanner for each path
142be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    int mUrisScanned;
143b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    Long mStartTime;
144be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
145b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    public BeamTransferManager(Context context, Callback callback,
146b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                               BeamTransferRecord pendingTransfer, boolean incoming) {
147be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mContext = context;
148be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mCallback = callback;
149be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mRemoteDevice = pendingTransfer.remoteDevice;
150b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        mIncoming = incoming;
151be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mTransferId = pendingTransfer.id;
1523ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales        mBluetoothTransferId = -1;
153b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        mDataLinkType = pendingTransfer.dataLinkType;
154b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        mRemoteActivating = pendingTransfer.remoteActivating;
155b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        mStartTime = 0L;
156d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        // For incoming transfers, count can be set later
157d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        mTotalCount = (pendingTransfer.uris != null) ? pendingTransfer.uris.length : 0;
158be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mLastUpdate = SystemClock.elapsedRealtime();
159be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mProgress = 0.0f;
160be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mState = STATE_NEW;
161b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        mUris = pendingTransfer.uris == null
162b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                ? new ArrayList<Uri>()
163b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                : new ArrayList<Uri>(Arrays.asList(pendingTransfer.uris));
164fbd59bd47482eb0784077ee3faf498d531497304Andres Morales        mTransferMimeTypes = new ArrayList<String>();
165be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mMimeTypes = new HashMap<String, String>();
166fbd59bd47482eb0784077ee3faf498d531497304Andres Morales        mPaths = new ArrayList<String>();
167be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mMediaUris = new HashMap<String, Uri>();
168b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        mCancelIntent = buildCancelIntent();
169be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mUrisScanned = 0;
170d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        mCurrentCount = 0;
171d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        mSuccessCount = 0;
172fbd59bd47482eb0784077ee3faf498d531497304Andres Morales        mOutgoingUris = pendingTransfer.uris;
173be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mHandler = new Handler(Looper.getMainLooper(), this);
174be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mHandler.sendEmptyMessageDelayed(MSG_TRANSFER_TIMEOUT, ALIVE_CHECK_MS);
175be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mNotificationManager = (NotificationManager) mContext.getSystemService(
176be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                Context.NOTIFICATION_SERVICE);
1773cdb596c8e3a19515545eb060696fa2a335b9db0Ruchi Kandoi        NotificationChannel notificationChannel = new NotificationChannel(
1783cdb596c8e3a19515545eb060696fa2a335b9db0Ruchi Kandoi                BEAM_NOTIFICATION_CHANNEL, mContext.getString(R.string.app_name),
1793cdb596c8e3a19515545eb060696fa2a335b9db0Ruchi Kandoi                NotificationManager.IMPORTANCE_HIGH);
1803cdb596c8e3a19515545eb060696fa2a335b9db0Ruchi Kandoi        mNotificationManager.createNotificationChannel(notificationChannel);
181be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
182be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
183be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    void whitelistOppDevice(BluetoothDevice device) {
184be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (DBG) Log.d(TAG, "Whitelisting " + device + " for BT OPP");
185b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        Intent intent = new Intent(ACTION_WHITELIST_DEVICE);
186073f0981e3920c085a57ff46f7f41bf329071c89Akinobu Nakashima        intent.setPackage(mContext.getString(R.string.bluetooth_package));
187be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
188be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
189be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
190be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
191b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    public void start() {
192b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        if (mStartTime > 0) {
193b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales            // already started
194b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales            return;
195b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        }
196b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
197b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        mStartTime = System.currentTimeMillis();
198b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
199b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        if (!mIncoming) {
200b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales            if (mDataLinkType == BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
201b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                new BluetoothOppHandover(mContext, mRemoteDevice, mUris, mRemoteActivating).start();
202b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales            }
203b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        }
204b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    }
205b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
206be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    public void updateFileProgress(float progress) {
207be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (!isRunning()) return; // Ignore when we're no longer running
208be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
209be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mHandler.removeMessages(MSG_NEXT_TRANSFER_TIMER);
210be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
211be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        this.mProgress = progress;
212be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
213be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        // We're still receiving data from this device - keep it in
214be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        // the whitelist for a while longer
215fbd59bd47482eb0784077ee3faf498d531497304Andres Morales        if (mIncoming && mRemoteDevice != null) whitelistOppDevice(mRemoteDevice);
216be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
217be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        updateStateAndNotification(STATE_IN_PROGRESS);
218be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
219be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
2203ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales    public synchronized void setBluetoothTransferId(int id) {
2213ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales        if (mBluetoothTransferId == -1 && id != -1) {
2223ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales            mBluetoothTransferId = id;
2233ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales            if (mState == STATE_CANCELLING) {
2243ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales                sendBluetoothCancelIntentAndUpdateState();
2253ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales            }
2263ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales        }
2273ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales    }
2283ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales
229be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    public void finishTransfer(boolean success, Uri uri, String mimeType) {
230be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (!isRunning()) return; // Ignore when we're no longer running
231be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
232d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        mCurrentCount++;
233be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (success && uri != null) {
234d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            mSuccessCount++;
235be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            if (DBG) Log.d(TAG, "Transfer success, uri " + uri + " mimeType " + mimeType);
236d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            mProgress = 0.0f;
237be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            if (mimeType == null) {
238fbd59bd47482eb0784077ee3faf498d531497304Andres Morales                mimeType = MimeTypeUtil.getMimeTypeForUri(mContext, uri);
239be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            }
240be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            if (mimeType != null) {
241fbd59bd47482eb0784077ee3faf498d531497304Andres Morales                mUris.add(uri);
242fbd59bd47482eb0784077ee3faf498d531497304Andres Morales                mTransferMimeTypes.add(mimeType);
243be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            } else {
244be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                if (DBG) Log.d(TAG, "Could not get mimeType for file.");
245be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            }
246be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        } else {
247be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            Log.e(TAG, "Handover transfer failed");
248be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            // Do wait to see if there's another file coming.
249be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
250be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mHandler.removeMessages(MSG_NEXT_TRANSFER_TIMER);
251d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        if (mCurrentCount == mTotalCount) {
252d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            if (mIncoming) {
253d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen                processFiles();
254d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            } else {
255d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen                updateStateAndNotification(mSuccessCount > 0 ? STATE_SUCCESS : STATE_FAILED);
256d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            }
257d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        } else {
258d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            mHandler.sendEmptyMessageDelayed(MSG_NEXT_TRANSFER_TIMER, WAIT_FOR_NEXT_TRANSFER_MS);
259d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            updateStateAndNotification(STATE_W4_NEXT_TRANSFER);
260d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        }
261be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
262be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
263be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    public boolean isRunning() {
2641c517e0f7d2162ec6bfbda964adf45ca69faa694george.kg_chang        if (mState != STATE_NEW && mState != STATE_IN_PROGRESS && mState != STATE_W4_NEXT_TRANSFER
2651c517e0f7d2162ec6bfbda964adf45ca69faa694george.kg_chang            && mState != STATE_CANCELLING) {
266be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            return false;
267be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        } else {
268be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            return true;
269be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
270be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
271be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
272d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen    public void setObjectCount(int objectCount) {
273d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        mTotalCount = objectCount;
274d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen    }
275d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen
276be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    void cancel() {
277be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (!isRunning()) return;
278be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
279be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        // Delete all files received so far
280fbd59bd47482eb0784077ee3faf498d531497304Andres Morales        for (Uri uri : mUris) {
281be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            File file = new File(uri.getPath());
282be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            if (file.exists()) file.delete();
283be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
284be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
2853ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales        if (mBluetoothTransferId != -1) {
2863ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales            // we know the ID, we can cancel immediately
2873ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales            sendBluetoothCancelIntentAndUpdateState();
2883ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales        } else {
2893ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales            updateStateAndNotification(STATE_CANCELLING);
2903ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales        }
2913ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales
2923ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales    }
2933ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales
2943ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales    private void sendBluetoothCancelIntentAndUpdateState() {
295b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        Intent cancelIntent = new Intent(ACTION_STOP_BLUETOOTH_TRANSFER);
296073f0981e3920c085a57ff46f7f41bf329071c89Akinobu Nakashima        cancelIntent.setPackage(mContext.getString(R.string.bluetooth_package));
297b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        cancelIntent.putExtra(BeamStatusReceiver.EXTRA_TRANSFER_ID, mBluetoothTransferId);
2983ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales        mContext.sendBroadcast(cancelIntent);
299be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        updateStateAndNotification(STATE_CANCELLED);
300be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
301be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
302be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    void updateNotification() {
3033cdb596c8e3a19515545eb060696fa2a335b9db0Ruchi Kandoi        Builder notBuilder = new Notification.Builder(mContext, BEAM_NOTIFICATION_CHANNEL);
304524777046191da324fc08881a103d0567cf91519Selim Cinek        notBuilder.setColor(mContext.getResources().getColor(
305524777046191da324fc08881a103d0567cf91519Selim Cinek                com.android.internal.R.color.system_notification_accent_color));
3061a0e18c0ba2236275fdc53a01adcf8d09b185c19Martijn Coenen        notBuilder.setWhen(mStartTime);
30797c433e213ed7ccdbac300da7577fbb9dd6cd1fcAndres Morales        notBuilder.setVisibility(Notification.VISIBILITY_PUBLIC);
3083cdb596c8e3a19515545eb060696fa2a335b9db0Ruchi Kandoi        notBuilder.setOnlyAlertOnce(true);
309d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        String beamString;
310d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        if (mIncoming) {
311d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            beamString = mContext.getString(R.string.beam_progress);
312d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        } else {
313d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            beamString = mContext.getString(R.string.beam_outgoing);
314d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        }
315be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (mState == STATE_NEW || mState == STATE_IN_PROGRESS ||
316be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                mState == STATE_W4_NEXT_TRANSFER || mState == STATE_W4_MEDIA_SCANNER) {
317be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            notBuilder.setAutoCancel(false);
31870fed93e7a684818495c6e27ab43fc05f7342926Martijn Coenen            notBuilder.setSmallIcon(mIncoming ? android.R.drawable.stat_sys_download :
31970fed93e7a684818495c6e27ab43fc05f7342926Martijn Coenen                    android.R.drawable.stat_sys_upload);
320d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            notBuilder.setTicker(beamString);
321d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            notBuilder.setContentTitle(beamString);
322be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            notBuilder.addAction(R.drawable.ic_menu_cancel_holo_dark,
323be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                    mContext.getString(R.string.cancel), mCancelIntent);
324d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            float progress = 0;
325d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            if (mTotalCount > 0) {
326d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen                float progressUnit = 1.0f / mTotalCount;
327d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen                progress = (float) mCurrentCount * progressUnit + mProgress * progressUnit;
328d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            }
329d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            if (mTotalCount > 0 && progress > 0) {
330d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen                notBuilder.setProgress(100, (int) (100 * progress), false);
331d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            } else {
332d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen                notBuilder.setProgress(100, 0, true);
333d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            }
334be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        } else if (mState == STATE_SUCCESS) {
335be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            notBuilder.setAutoCancel(true);
33670fed93e7a684818495c6e27ab43fc05f7342926Martijn Coenen            notBuilder.setSmallIcon(mIncoming ? android.R.drawable.stat_sys_download_done :
33770fed93e7a684818495c6e27ab43fc05f7342926Martijn Coenen                    android.R.drawable.stat_sys_upload_done);
338be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            notBuilder.setTicker(mContext.getString(R.string.beam_complete));
339be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            notBuilder.setContentTitle(mContext.getString(R.string.beam_complete));
340be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
341d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            if (mIncoming) {
342b2e735d7e1de4fda8b4d34096b8dce275fe52844Martijn Coenen                notBuilder.setContentText(mContext.getString(R.string.beam_tap_to_view));
343d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen                Intent viewIntent = buildViewIntent();
344d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen                PendingIntent contentIntent = PendingIntent.getActivity(
345d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen                        mContext, mTransferId, viewIntent, 0, null);
346be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
347d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen                notBuilder.setContentIntent(contentIntent);
348d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen            }
349be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        } else if (mState == STATE_FAILED) {
350be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            notBuilder.setAutoCancel(false);
35170fed93e7a684818495c6e27ab43fc05f7342926Martijn Coenen            notBuilder.setSmallIcon(mIncoming ? android.R.drawable.stat_sys_download_done :
35270fed93e7a684818495c6e27ab43fc05f7342926Martijn Coenen                    android.R.drawable.stat_sys_upload_done);
353be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            notBuilder.setTicker(mContext.getString(R.string.beam_failed));
354be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            notBuilder.setContentTitle(mContext.getString(R.string.beam_failed));
3553ee12128efdf8f96d7921429b4b9e20b55943eb6Andres Morales        } else if (mState == STATE_CANCELLED || mState == STATE_CANCELLING) {
356be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            notBuilder.setAutoCancel(false);
35770fed93e7a684818495c6e27ab43fc05f7342926Martijn Coenen            notBuilder.setSmallIcon(mIncoming ? android.R.drawable.stat_sys_download_done :
35870fed93e7a684818495c6e27ab43fc05f7342926Martijn Coenen                    android.R.drawable.stat_sys_upload_done);
359be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            notBuilder.setTicker(mContext.getString(R.string.beam_canceled));
360be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            notBuilder.setContentTitle(mContext.getString(R.string.beam_canceled));
361be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        } else {
362be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            return;
363be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
364be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
365be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mNotificationManager.notify(null, mTransferId, notBuilder.build());
366be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
367be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
368be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    void updateStateAndNotification(int newState) {
369be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        this.mState = newState;
370be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        this.mLastUpdate = SystemClock.elapsedRealtime();
371be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
372f5a9550725ea1882b9e03d2e5f92c995d09cd84bMartijn Coenen        mHandler.removeMessages(MSG_TRANSFER_TIMEOUT);
373f5a9550725ea1882b9e03d2e5f92c995d09cd84bMartijn Coenen        if (isRunning()) {
374f5a9550725ea1882b9e03d2e5f92c995d09cd84bMartijn Coenen            // Update timeout timer if we're still running
375be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            mHandler.sendEmptyMessageDelayed(MSG_TRANSFER_TIMEOUT, ALIVE_CHECK_MS);
376be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
377be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
378be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        updateNotification();
379be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
380be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if ((mState == STATE_SUCCESS || mState == STATE_FAILED || mState == STATE_CANCELLED)
381be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                && !mCalledBack) {
382be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            mCalledBack = true;
383be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            // Notify that we're done with this transfer
384be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            mCallback.onTransferComplete(this, mState == STATE_SUCCESS);
385be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
386be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
387be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
388be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    void processFiles() {
389be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        // Check the amount of files we received in this transfer;
390be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        // If more than one, create a separate directory for it.
391be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        String extRoot = Environment.getExternalStorageDirectory().getPath();
392be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        File beamPath = new File(extRoot + "/" + BEAM_DIR);
393be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
394fbd59bd47482eb0784077ee3faf498d531497304Andres Morales        if (!checkMediaStorage(beamPath) || mUris.size() == 0) {
395be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            Log.e(TAG, "Media storage not valid or no uris received.");
396be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            updateStateAndNotification(STATE_FAILED);
397be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            return;
398be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
399be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
400fbd59bd47482eb0784077ee3faf498d531497304Andres Morales        if (mUris.size() > 1) {
401be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            beamPath = generateMultiplePath(extRoot + "/" + BEAM_DIR + "/");
402be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            if (!beamPath.isDirectory() && !beamPath.mkdir()) {
403be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                Log.e(TAG, "Failed to create multiple path " + beamPath.toString());
404be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                updateStateAndNotification(STATE_FAILED);
405be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                return;
406be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            }
407be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
408be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
409fbd59bd47482eb0784077ee3faf498d531497304Andres Morales        for (int i = 0; i < mUris.size(); i++) {
410fbd59bd47482eb0784077ee3faf498d531497304Andres Morales            Uri uri = mUris.get(i);
411fbd59bd47482eb0784077ee3faf498d531497304Andres Morales            String mimeType = mTransferMimeTypes.get(i);
412be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
413be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            File srcFile = new File(uri.getPath());
414be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
415be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            File dstFile = generateUniqueDestination(beamPath.getAbsolutePath(),
416be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                    uri.getLastPathSegment());
417fbd59bd47482eb0784077ee3faf498d531497304Andres Morales            Log.d(TAG, "Renaming from " + srcFile);
418be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            if (!srcFile.renameTo(dstFile)) {
419be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                if (DBG) Log.d(TAG, "Failed to rename from " + srcFile + " to " + dstFile);
420be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                srcFile.delete();
421be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                return;
422be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            } else {
423be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                mPaths.add(dstFile.getAbsolutePath());
424be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                mMimeTypes.put(dstFile.getAbsolutePath(), mimeType);
425be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                if (DBG) Log.d(TAG, "Did successful rename from " + srcFile + " to " + dstFile);
426be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            }
427be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
428be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
429be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        // We can either add files to the media provider, or provide an ACTION_VIEW
430be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        // intent to the file directly. We base this decision on the mime type
431be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        // of the first file; if it's media the platform can deal with,
432be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        // use the media provider, if it's something else, just launch an ACTION_VIEW
433be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        // on the file.
434be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        String mimeType = mMimeTypes.get(mPaths.get(0));
435be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (mimeType.startsWith("image/") || mimeType.startsWith("video/") ||
436be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                mimeType.startsWith("audio/")) {
437be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            String[] arrayPaths = new String[mPaths.size()];
438be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            MediaScannerConnection.scanFile(mContext, mPaths.toArray(arrayPaths), null, this);
439be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            updateStateAndNotification(STATE_W4_MEDIA_SCANNER);
440be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        } else {
441be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            // We're done.
442be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            updateStateAndNotification(STATE_SUCCESS);
443be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
444be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
445be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
446be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
447be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    public boolean handleMessage(Message msg) {
448be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (msg.what == MSG_NEXT_TRANSFER_TIMER) {
449be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            // We didn't receive a new transfer in time, finalize this one
450be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            if (mIncoming) {
451be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                processFiles();
452be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            } else {
453d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen                updateStateAndNotification(mSuccessCount > 0 ? STATE_SUCCESS : STATE_FAILED);
454be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            }
455be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            return true;
456be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        } else if (msg.what == MSG_TRANSFER_TIMEOUT) {
457f5a9550725ea1882b9e03d2e5f92c995d09cd84bMartijn Coenen            // No update on this transfer for a while, fail it.
458f5a9550725ea1882b9e03d2e5f92c995d09cd84bMartijn Coenen            if (DBG) Log.d(TAG, "Transfer timed out for id: " + Integer.toString(mTransferId));
459f5a9550725ea1882b9e03d2e5f92c995d09cd84bMartijn Coenen            updateStateAndNotification(STATE_FAILED);
460be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
461be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        return false;
462be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
463be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
464be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    public synchronized void onScanCompleted(String path, Uri uri) {
465be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (DBG) Log.d(TAG, "Scan completed, path " + path + " uri " + uri);
466be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (uri != null) {
467be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            mMediaUris.put(path, uri);
468be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
469be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        mUrisScanned++;
470be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (mUrisScanned == mPaths.size()) {
471be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            // We're done
472be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            updateStateAndNotification(STATE_SUCCESS);
473be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
474be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
475be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
476be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
477be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    Intent buildViewIntent() {
478be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (mPaths.size() == 0) return null;
479be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
480be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        Intent viewIntent = new Intent(Intent.ACTION_VIEW);
481be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
482be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        String filePath = mPaths.get(0);
483be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        Uri mediaUri = mMediaUris.get(filePath);
484be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        Uri uri =  mediaUri != null ? mediaUri :
485d5567fc2ff2d4e462932b40450f536b7e1a3bf3bJohan Redestig            FileProvider.getUriForFile(mContext, "com.google.android.nfc.fileprovider",
486d5567fc2ff2d4e462932b40450f536b7e1a3bf3bJohan Redestig                    new File(filePath));
487be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        viewIntent.setDataAndTypeAndNormalize(uri, mMimeTypes.get(filePath));
488d5567fc2ff2d4e462932b40450f536b7e1a3bf3bJohan Redestig        viewIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
489d5567fc2ff2d4e462932b40450f536b7e1a3bf3bJohan Redestig                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
490be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        return viewIntent;
491be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
492be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
493b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    PendingIntent buildCancelIntent() {
494b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        Intent intent = new Intent(BeamStatusReceiver.ACTION_CANCEL_HANDOVER_TRANSFER);
495b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        intent.putExtra(BeamStatusReceiver.EXTRA_ADDRESS, mRemoteDevice.getAddress());
496b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        intent.putExtra(BeamStatusReceiver.EXTRA_INCOMING, mIncoming ?
497b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                BeamStatusReceiver.DIRECTION_INCOMING : BeamStatusReceiver.DIRECTION_OUTGOING);
498d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen        PendingIntent pi = PendingIntent.getBroadcast(mContext, mTransferId, intent,
499d72546a022862a277e43499849e275e4e5ea209aMartijn Coenen                PendingIntent.FLAG_ONE_SHOT);
500be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
501be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        return pi;
502be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
503be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
504b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    static boolean checkMediaStorage(File path) {
505b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
506b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales            if (!path.isDirectory() && !path.mkdir()) {
507b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                Log.e(TAG, "Not dir or not mkdir " + path.getAbsolutePath());
508b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                return false;
509b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales            }
510b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales            return true;
511b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        } else {
512b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales            Log.e(TAG, "External storage not mounted, can't store file.");
513b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales            return false;
514b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        }
515b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    }
516b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
517b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    static File generateUniqueDestination(String path, String fileName) {
518be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        int dotIndex = fileName.lastIndexOf(".");
519be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        String extension = null;
520be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        String fileNameWithoutExtension = null;
521be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        if (dotIndex < 0) {
522be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            extension = "";
523be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            fileNameWithoutExtension = fileName;
524be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        } else {
525be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            extension = fileName.substring(dotIndex);
526be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            fileNameWithoutExtension = fileName.substring(0, dotIndex);
527be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
528be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        File dstFile = new File(path + File.separator + fileName);
529be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        int count = 0;
530be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        while (dstFile.exists()) {
531be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            dstFile = new File(path + File.separator + fileNameWithoutExtension + "-" +
532be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                    Integer.toString(count) + extension);
533be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            count++;
534be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
535be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        return dstFile;
536be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
537be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
538b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    static File generateMultiplePath(String beamRoot) {
539be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        // Generate a unique directory with the date
540be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        String format = "yyyy-MM-dd";
541116dfa0107cdc7ab098ed95828280542bf6b0a1eThe Android Open Source Project        SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.US);
542be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        String newPath = beamRoot + "beam-" + sdf.format(new Date());
543be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        File newFile = new File(newPath);
544be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        int count = 0;
545be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        while (newFile.exists()) {
546be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            newPath = beamRoot + "beam-" + sdf.format(new Date()) + "-" +
547be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                    Integer.toString(count);
548be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            newFile = new File(newPath);
549be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project            count++;
550be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        }
551be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project        return newFile;
552be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project    }
553be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project}
554be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project
555