Merge lp:~humpolec-team/humpolec/UbuntuInstaller-refactor into lp:humpolec

Proposed by Ondrej Kubik
Status: Merged
Merged at revision: 72
Proposed branch: lp:~humpolec-team/humpolec/UbuntuInstaller-refactor
Merge into: lp:humpolec
Diff against target: 5595 lines (+2511/-1557) (has conflicts)
37 files modified
.bzrignore (+1/-1)
Android.mk (+1/-1)
AndroidManifest.xml (+38/-12)
ant.properties (+21/-0)
assets/archive-master.tar.xz.asc (+0/-17)
assets/system-image-upgrader (+28/-156)
assets/u-reboot-app.tar.xz.asc (+0/-17)
assets/upgrade-checker (+0/-16)
build.xml (+35/-37)
check-sdk.sh (+10/-0)
make_release.sh (+0/-16)
project.properties (+1/-1)
res/layout/ubuntu_dualboot_launch.xml (+152/-104)
res/menu/installer_menu.xml (+6/-10)
res/menu/launcher_menu.xml (+19/-10)
res/values/attrs.xml (+4/-4)
res/values/colors.xml (+4/-4)
res/values/ids.xml (+26/-0)
res/values/strings.xml (+70/-14)
res/values/styles.xml (+4/-4)
src/com/canonical/ubuntu/installer/BaseActivity.java (+70/-0)
src/com/canonical/ubuntu/installer/BootReceiver.java (+39/-0)
src/com/canonical/ubuntu/installer/InstallActivity.java (+184/-160)
src/com/canonical/ubuntu/installer/JsonChannelParser.java (+38/-16)
src/com/canonical/ubuntu/installer/LaunchActivity.java (+216/-52)
src/com/canonical/ubuntu/installer/NumberPickerDialog.java (+17/-3)
src/com/canonical/ubuntu/installer/TextPickerDialog.java (+17/-1)
src/com/canonical/ubuntu/installer/UbuntuInstallService.java (+1237/-723)
src/com/canonical/ubuntu/installer/Utils.java (+245/-9)
src/com/canonical/ubuntu/installer/VersionInfo.java (+0/-141)
src/com/canonical/ubuntu/widget/UbuntuButton.java (+4/-4)
src/com/canonical/ubuntu/widget/UbuntuCheckBox.java (+4/-4)
src/com/canonical/ubuntu/widget/UbuntuCheckBoxPreference.java (+4/-4)
src/com/canonical/ubuntu/widget/UbuntuEditText.java (+4/-4)
src/com/canonical/ubuntu/widget/UbuntuEditTextPreference.java (+4/-4)
src/com/canonical/ubuntu/widget/UbuntuPreference.java (+4/-4)
src/com/canonical/ubuntu/widget/UbuntuTextView.java (+4/-4)
Text conflict in AndroidManifest.xml
To merge this branch: bzr merge lp:~humpolec-team/humpolec/UbuntuInstaller-refactor
Reviewer Review Type Date Requested Status
Ondrej Kubik Approve
Review via email: mp+222493@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Ondrej Kubik (ondrak) wrote :

This has been dog food tested and released as version 1.0

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2013-12-08 16:01:23 +0000
3+++ .bzrignore 2014-06-09 11:59:50 +0000
4@@ -1,6 +1,6 @@
5+.DS_Store
6 .project
7 local.properties
8 bin
9 gen
10-android-ubuntu-launcher.apk
11
12
13=== modified file 'Android.mk'
14--- Android.mk 2013-12-08 16:01:23 +0000
15+++ Android.mk 2014-06-09 11:59:50 +0000
16@@ -1,4 +1,4 @@
17-# UFA updater
18+# Dualboot installer
19 LOCAL_PATH:= $(call my-dir)
20
21 include $(CLEAR_VARS)
22
23=== modified file 'AndroidManifest.xml'
24--- AndroidManifest.xml 2014-03-05 15:35:14 +0000
25+++ AndroidManifest.xml 2014-06-09 11:59:50 +0000
26@@ -1,12 +1,34 @@
27 <?xml version="1.0" encoding="utf-8"?>
28+<!--
29+/*
30+ * This file is part of Ubuntu dualboot installer for Android.
31+ * Copyright 2013 Canonical Ltd.
32+ *
33+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
34+ * it under the terms of the GNU General Public License as published by
35+ * the Free Software Foundation.
36+ *
37+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
38+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
39+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
40+ * See the GNU General Public License for more details.
41+ *
42+ * You should have received a copy of the GNU General Public License
43+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
44+ */
45+-->
46 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
47 package="com.canonical.ubuntu.installer"
48 android:versionCode="20"
49+<<<<<<< TREE
50 android:versionName="0.3" >
51+=======
52+ android:versionName="1.0.0" >
53+>>>>>>> MERGE-SOURCE
54
55 <uses-sdk
56 android:minSdkVersion="17"
57- android:targetSdkVersion="18" />
58+ android:targetSdkVersion="19" />
59 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
60 <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
61 <uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
62@@ -20,8 +42,6 @@
63 <uses-permission android:name="android.permission.WAKE_LOCK" />
64 <uses-permission android:name="android.permission.RECORD_AUDIO" />
65 <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
66- <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
67- <uses-permission android:name="android.permission.REBOOT"/>
68 <uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
69
70 <application
71@@ -46,23 +66,29 @@
72 android:label="@string/app_name">
73 </activity>
74
75- <service android:name="com.canonical.ubuntu.installer.UbuntuInstallService">
76+ <receiver android:name=".BootReceiver" >
77+ <intent-filter>
78+ <action android:name="android.intent.action.BOOT_COMPLETED" />
79+ </intent-filter>
80+ </receiver>
81+
82+ <service android:name="com.canonical.ubuntu.installer.UbuntuInstallService"
83+ android:exported="false">
84 <intent-filter>
85 <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.GET_CHANNEL_LIST" />
86 <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.DOWNLOAD_RELEASE" />
87- <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.CANCEL_DOWNLOAD" />
88+ <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.DOWNLOAD_UPDATE" />
89 <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.PAUSE_DOWNLOAD" />
90 <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.RESUME_DOWNLOAD" />
91- <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.CLEAN_DOWNLOADED" />
92+ <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.DELETE_DOWNLOAD" />
93 <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.INSTALL_UBUNTU" />
94- <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.CANCEL_INSTALL" />
95 <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.UINSTALL_UBUNTU" />
96- <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.CHECK_FOR_UPDATE" />
97- <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.DELETE_USER_DATA" />
98- <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.GET_SERVICE_STATE" />
99- <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.GET_PROGRESS_STATUS" />
100+ <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.DELETE_UBUNTU_USER_DATA" />
101 <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.REBOOT_UBUNTU" />
102- <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.IS_UBUNTU_UPGRADABLE" />
103+ <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.UPDATE_STORAGE_USE" />
104+ <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.CHECK_IF_UPDATE_AVAILABLE" />
105+ <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.CHECK_PENDING_UPDATES" />
106+ <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.PREPARE_ANDROID_UPDATE" />
107 </intent-filter>
108 </service>
109 </application>
110
111=== added file 'ant.properties'
112--- ant.properties 1970-01-01 00:00:00 +0000
113+++ ant.properties 2014-06-09 11:59:50 +0000
114@@ -0,0 +1,21 @@
115+# This file is used to override default values used by the Ant build system.
116+#
117+# This file must be checked in Version Control Systems, as it is
118+# integral to the build system of your project.
119+
120+# This file is only used by the Ant script.
121+
122+# You can use this to override default values such as
123+# 'source.dir' for the location of your java source folder and
124+# 'out.dir' for the location of your output folder.
125+
126+# You can also use it define how the release builds are signed by declaring
127+# the following properties:
128+# 'key.store' for the location of your keystore and
129+# 'key.alias' for the name of the key to use.
130+# The password will be asked during the build when you use the 'release' target.
131+
132+java.encoding=UTF-8
133+
134+key.store=../humpolec.keystore
135+key.alias=humpolec
136
137=== removed file 'assets/archive-master.tar.xz'
138Binary files assets/archive-master.tar.xz 2013-12-08 16:01:23 +0000 and assets/archive-master.tar.xz 1970-01-01 00:00:00 +0000 differ
139=== removed file 'assets/archive-master.tar.xz.asc'
140--- assets/archive-master.tar.xz.asc 2013-12-08 16:01:23 +0000
141+++ assets/archive-master.tar.xz.asc 1970-01-01 00:00:00 +0000
142@@ -1,17 +0,0 @@
143------BEGIN PGP SIGNATURE-----
144-Version: GnuPG v1.4.12 (GNU/Linux)
145-
146-iQIcBAABAgAGBQJRzXznAAoJEAv7hH8/Jy9b9MEP/iTl7lfFQNOu+bR+hRHOh40B
147-trXCwTeKYGLL98h7pbqrqFD5QppGWZ5Kpyh5QeNB7yU8Jk5bcIQg3iTBg4kQhbjZ
148-CvfBAk5goKDzHF+tx2HWFmHie3kxDlOQ39dWqr38Q78elC1VtRfiWQNbz7E2955D
149-d5/Lo+THwKS0aYD8RR/wHbiEnIiOSxZLZo9Bdkxlty9LXbY16SUPY3R7POGdds0S
150-OvQmsmFsjoD4b68aTjw0Fx12EQ1fDBycBmxNbgQ6E/xKf78Ttsp8gRaoopdr6odH
151-FZWUCv0Vr7mrbSr8MrotydVRth5K/FJv4xnfTI9rY50PZqA72xFNfQyQa35/qfuZ
152-3E+d9DdPk8KtovNCploTufRG5zvkXOn+rGJQCicLh4H2L7+YO5ZwHbDQFTDUrAhl
153-0wlqaqUuwYOC7fxk3SHA3IINZ97LCUYfPWZRUUVOM2uia4BhI0TfRj09I+HnJM+I
154-cNlfjmzCedlLjMwgBOw6tj1l3FEPl/aEwCTs1Oc7dc05JPDNcRqdks45NnSS/Xg7
155-pNHPS6wED0zYAKQeBDkji7Gf+9gR3eVWWbArSzbkqyCeWhgPyqwjTUQNMx6PIPNm
156-0lszQWe50Y9mOuwcRd2gA293zVjsnr7NxOJPhgZsHB9fRaZtSg7vWVOo1jopkPPZ
157-Vt1nrVFGkrss7EJ+Vc53
158-=Sszb
159------END PGP SIGNATURE-----
160
161=== added file 'assets/recovery-remount.tar.xz'
162Binary files assets/recovery-remount.tar.xz 1970-01-01 00:00:00 +0000 and assets/recovery-remount.tar.xz 2014-06-09 11:59:50 +0000 differ
163=== modified file 'assets/system-image-upgrader'
164--- assets/system-image-upgrader 2014-01-08 06:21:14 +0000
165+++ assets/system-image-upgrader 2014-06-09 11:59:50 +0000
166@@ -9,7 +9,7 @@
167 fi
168
169 BUSYBOX=busybox
170-COMMAND_FILE=$1.applying
171+COMMAND_FILE=$1
172 PRIVATE_DIR=$2
173 REMOVE_LIST="$COMMAND_FILE"
174 TAR=u_tar
175@@ -22,129 +22,14 @@
176
177 # switch to the update command folder, which has the images
178 cd $UPDATE_FOLDER
179-mv $1 $1.applying
180-
181
182 # Functions
183-verify_signature() {
184- return 0
185- # $1 => validation keyring name
186- # $2 => path to validate
187-
188- if [ ! -e $2 ]; then
189- echo "File doesn't exist: $2"
190- return 1
191- fi
192-
193- # Check against the blacklist
194- if [ -e ${TMP}/system-image/blacklist/pubring.gpg ]; then
195- export GNUPGHOME=${TMP}/system-image/blacklist/
196- if gpg --verify $2 >/dev/null 2>&1; then
197- echo "File signed by a blacklisted key: $2"
198- return 1
199- fi
200- fi
201-
202- # Check against the keyring
203- export GNUPGHOME=${TMP}/system-image/$1/
204- if [ ! -e "$GNUPGHOME" ]; then
205- echo "Keyring doesn't exist: $1"
206- return 1
207- fi
208-
209- if gpg --verify $2 >/dev/null 2>&1; then
210- return 0
211- fi
212-}
213-
214-install_keyring() {
215- # $1 => full path to tarball
216- # $2 => full path to signature
217-
218- # Some basic checks
219- if [ ! -e "$1" ] || [ ! -e "$2" ]; then
220- echo "Missing keyring files: $1 => $2"
221- return 1
222- fi
223-
224- # Unpacking
225- TMPDIR=$($BUSYBOX mktemp -d ${TMP}/tempdir.XXXXXXXX)
226- cd $TMPDIR
227- cat $1 | $BUSYBOX unxz | $TAR xf -
228- if [ ! -e keyring.json ] || [ ! -e keyring.gpg ]; then
229- rm -Rf $TMPDIR
230- echo "Invalid keyring: $1"
231- return 1
232- fi
233-
234- # Extract the expiry
235- keyring_expiry=$(grep "^ \"expiry\": " keyring.json | $BUSYBOX cut -d: -f2 | $BUSYBOX sed -e "s/[ \",]//g")
236- if [ -n "$keyring_expiry" ] && [ "$keyring_expiry" -lt "$(date +%s)" ]; then
237- rm -Rf $TMPDIR
238- echo "Keyring expired: $1"
239- return 1
240- fi
241-
242- # Extract the keyring type
243- keyring_type=$(grep "^ \"type\": " keyring.json | $BUSYBOX cut -d: -f2 | $BUSYBOX sed -e "s/[, \"]//g")
244- if [ -z "$keyring_type" ]; then
245- rm -Rf $TMPDIR
246- echo "Missing keyring type: $1"
247- return 1
248- fi
249-
250- if [ -e ${TMP}/system-image/$keyring_type ]; then
251- rm -Rf $TMPDIR
252- echo "Keyring already loaded: $1"
253- return 1
254- fi
255-
256- signer="unknown"
257- case "$keyring_type" in
258- archive-master)
259- signer=""
260- ;;
261-
262- image-master)
263- signer="archive-master"
264- ;;
265-
266- image-signing|blacklist)
267- signer="image-master"
268- ;;
269-
270- device-signing)
271- signer="image-signing"
272- ;;
273- esac
274-
275- if [ -n "$signer" ] && ! verify_signature $signer $2; then
276- rm -Rf $TMPDIR
277- echo "Invalid signature: $1"
278- return 1
279- fi
280-
281- mkdir ${TMP}/system-image/$keyring_type
282- chmod 700 ${TMP}/system-image/$keyring_type
283- mv $TMPDIR/keyring.gpg ${TMP}/system-image/$keyring_type/pubring.gpg
284- chmod 600 ${TMP}/system-image/$keyring_type/pubring.gpg
285- chown 0:0 ${TMP}/system-image/$keyring_type/pubring.gpg
286- rm -Rf $TMPDIR
287- return 0
288-}
289-
290-# print out if we will need to create SWAP file
291-if [ ! -e /data/SWAP.img ]; then
292- echo "SWAP-file-missing" >&2
293-fi
294+
295
296 # Initialize GPG
297 rm -Rf ${TMP}/system-image
298 mkdir -p ${TMP}/system-image
299-if [ -e ${PWD}/archive-master.tar.xz ]; then
300- echo "Loading keyring: archive-master.tar.xz"
301- install_keyring ${PWD}/archive-master.tar.xz ${PWD}/archive-master.tar.xz.asc
302-fi
303+
304
305 # Process the command file
306 FULL_IMAGE=0
307@@ -159,11 +44,14 @@
308 system)
309 FULL_IMAGE=1
310 rm -f /data/system.img
311+ echo "Creating new system filesystem"
312 $BUSYBOX dd if=/dev/zero of=/data/system.img seek=500K bs=4096 count=1
313 $BUSYBOX mkfs.ext2 -F /data/system.img
314+ echo "New system filesystem created"
315 ;;
316
317 data)
318+ # formatting data partion -> just detele ubuntu bits
319 rm -f /data/SWAP.img
320 rm -Rf /data/system-data
321 rm -Rf /data/user-data
322@@ -176,25 +64,11 @@
323 ;;
324
325 load_keyring)
326- if [ ! -e "$UPDATE_FOLDER/$2" ] || [ ! -e "$UPDATE_FOLDER/$3" ]; then
327- echo "Skipping missing file: $2"
328- continue
329- fi
330- REMOVE_LIST="$REMOVE_LIST $UPDATE_FOLDER/$2 $UPDATE_FOLDER/$3"
331-
332- echo "Loading keyring: $2"
333- install_keyring $UPDATE_FOLDER/$2 $UPDATE_FOLDER/$3
334-
335- if [ -e ${TMP}/system-image/image-master/pubring.gpg ] && \
336- [ ! -e ${TMP}/system-image/blacklist/pubring.gpg ] && \
337- [ -e /data/system-data/var/lib/system-image/blacklist.tar.xz ] && \
338- [ -e /data/system-data/var/lib/system-image/blacklist.tar.xz.asc ]; then
339- echo "Loading blacklist keyring"
340- install_keyring /data/system-data/var/lib/system-image/blacklist.tar.xz /data/system-data/var/lib/system-image/blacklist.tar.xz.asc
341- fi
342+ echo "Loading keys: $2 $3"
343 ;;
344
345 mount)
346+ echo "Mounting: $2"
347 case "$2" in
348 system)
349 mkdir -p /cache/system
350@@ -208,8 +82,11 @@
351 ;;
352
353 unmount)
354+ echo "Unmounting: $2"
355 case "$2" in
356 system)
357+ # print out version info
358+ cat /cache/system/etc/system-image/channel.ini || true
359 aloopmount umount /cache/system && echo .
360 rmdir /cache/system
361 ;;
362@@ -221,17 +98,13 @@
363 ;;
364
365 update)
366- if [ ! -e "$UPDATE_FOLDER/$2" ] || [ ! -e "$UPDATE_FOLDER/$3" ]; then
367+ if [ ! -e "$UPDATE_FOLDER/$2" ]; then
368 echo "Skipping missing file: $2"
369 continue
370 fi
371
372- REMOVE_LIST="$REMOVE_LIST $UPDATE_FOLDER/$2 $UPDATE_FOLDER/$3"
373- if ! verify_signature device-signing $UPDATE_FOLDER/$2 && \
374- ! verify_signature image-signing $UPDATE_FOLDER/$2; then
375- echo "Invalid signature"
376- continue
377- fi
378+ REMOVE_LIST="$REMOVE_LIST $UPDATE_FOLDER/$2"
379+
380
381 echo "Applying update: $2"
382 cd /cache
383@@ -239,17 +112,19 @@
384
385 # Start by removing any file listed in "removed"
386 if [ "$FULL_IMAGE" != "1" ]; then
387- cat $UPDATE_FOLDER/$2 | $BUSYBOX unxz | $TAR xf - removed >/dev/null 2>&1 || true
388+ $BUSYBOX cat $UPDATE_FOLDER/$2 | $BUSYBOX unxz | $TAR --checkpoint=400 -xf - removed >/dev/null || true
389 if [ -e removed ]; then
390- while read file; do
391+ echo "Removing old files..."
392+ while read file; do
393 rm -Rf $file
394 done < removed
395 fi
396 rm -f removed
397+ echo "Adding new files..."
398 fi
399
400 # Unpack everything else on top of the system partition
401- cat $UPDATE_FOLDER/$2 | $BUSYBOX unxz | $TAR --checkpoint=200 -xf -
402+ $BUSYBOX cat $UPDATE_FOLDER/$2 | $BUSYBOX unxz | $TAR --checkpoint=200 -xf -
403 rm -f removed
404
405 if [ -e "partitions/boot.img" ]; then
406@@ -281,20 +156,17 @@
407
408 # Remove the update files
409 for file in $REMOVE_LIST; do
410- echo rm $file
411+ rm $file
412 done
413
414-# Create the SWAP image if missing
415-if [ ! -e /data/SWAP.img ]; then
416- echo "Creating SWAP device."
417- echo "SWAP Checkpoint 0" >&2
418- # create swap file in private folder, so service can watch progress
419- dd if=/dev/zero of=$PRIVATE_DIR/SWAP.img bs=4096 count=131072
420- echo "Moving SWAP device to final destination" >&2
421- mv $PRIVATE_DIR/SWAP.img /data/
422- echo "Calling mkswap on /data/SWAP.img" >&2
423- $BUSYBOX mkswap /data/SWAP.img
424-fi
425+# Ensure we have sane permissions
426+chmod 600 /data/system.img
427+chown 0:0 /data/system.img
428+
429+# calculate sizes
430+busybox du -sm /data/system.img || true
431+busybox du -sm /data/system-data || true
432+busybox du -sm /data/user-data || true
433
434 touch /data/.last_update || true
435 sync
436
437=== removed file 'assets/u-reboot-app.tar.xz.asc'
438--- assets/u-reboot-app.tar.xz.asc 2013-12-12 02:02:35 +0000
439+++ assets/u-reboot-app.tar.xz.asc 1970-01-01 00:00:00 +0000
440@@ -1,17 +0,0 @@
441------BEGIN PGP SIGNATURE-----
442-Version: GnuPG v1.4.12 (GNU/Linux)
443-
444-iQIcBAABAgAGBQJRzXznAAoJEAv7hH8/Jy9b9MEP/iTl7lfFQNOu+bR+hRHOh40B
445-trXCwTeKYGLL98h7pbqrqFD5QppGWZ5Kpyh5QeNB7yU8Jk5bcIQg3iTBg4kQhbjZ
446-CvfBAk5goKDzHF+tx2HWFmHie3kxDlOQ39dWqr38Q78elC1VtRfiWQNbz7E2955D
447-d5/Lo+THwKS0aYD8RR/wHbiEnIiOSxZLZo9Bdkxlty9LXbY16SUPY3R7POGdds0S
448-OvQmsmFsjoD4b68aTjw0Fx12EQ1fDBycBmxNbgQ6E/xKf78Ttsp8gRaoopdr6odH
449-FZWUCv0Vr7mrbSr8MrotydVRth5K/FJv4xnfTI9rY50PZqA72xFNfQyQa35/qfuZ
450-3E+d9DdPk8KtovNCploTufRG5zvkXOn+rGJQCicLh4H2L7+YO5ZwHbDQFTDUrAhl
451-0wlqaqUuwYOC7fxk3SHA3IINZ97LCUYfPWZRUUVOM2uia4BhI0TfRj09I+HnJM+I
452-cNlfjmzCedlLjMwgBOw6tj1l3FEPl/aEwCTs1Oc7dc05JPDNcRqdks45NnSS/Xg7
453-pNHPS6wED0zYAKQeBDkji7Gf+9gR3eVWWbArSzbkqyCeWhgPyqwjTUQNMx6PIPNm
454-0lszQWe50Y9mOuwcRd2gA293zVjsnr7NxOJPhgZsHB9fRaZtSg7vWVOo1jopkPPZ
455-Vt1nrVFGkrss7EJ+Vc53
456-=Sszb
457------END PGP SIGNATURE-----
458
459=== removed file 'assets/upgrade-checker'
460--- assets/upgrade-checker 2013-12-25 11:32:41 +0000
461+++ assets/upgrade-checker 1970-01-01 00:00:00 +0000
462@@ -1,16 +0,0 @@
463-#!/system/bin/sh
464-# Copyright (C) 2013 Canonical Ltd.
465-#
466-# Check if ubuntu_command exist
467-# print the path and exit 0 if found.
468-
469-FILES=$@
470-
471-for file in $FILES ; do
472- if [[ -f $file ]] ; then
473- echo $file
474- exit 1
475- fi
476-done
477-
478-exit 0
479
480=== modified file 'build.xml'
481--- build.xml 2013-12-08 16:01:23 +0000
482+++ build.xml 2014-06-09 11:59:50 +0000
483@@ -1,31 +1,40 @@
484 <?xml version="1.0" encoding="UTF-8"?>
485-<project name="MainActivity" default="help">
486-
487- <!-- The local.properties file is created and updated by the 'android' tool.
488- It contains the path to the SDK. It should *NOT* be checked into
489- Version Control Systems. -->
490+<project name="UbuntuInstaller" default="help">
491+
492+ <description>
493+ *** Humpolec - Ubuntu Dual Boot for Android ***
494+
495+ http://launchpad.net/humpolec
496+
497+ Ubuntu Dual Boot is provided to developers who want to contribute to development
498+ of Ubuntu for Phones and give them an ability to run Ubuntu and Android on
499+ a single device. It is not intended to be used by regular users.
500+
501+ Make sure you have Android SDK installed!
502+
503+ Contact:
504+ Ondrej Kubik ondrej.kubik@canonical.com
505+ Michal Karnicki michal.karnicki@canonical.com
506+ </description>
507+
508+ <!-- Check if android is present. -->
509+ <exec executable="./check-sdk.sh" failonerror="true">
510+ </exec>
511+
512+ <property file="project.properties" />
513+
514+ <exec executable="android">
515+ <arg value="update" />
516+ <arg value="project" />
517+ <arg value="-p" />
518+ <arg value="." />
519+ <arg value="-t" />
520+ <arg value="${target}" />
521+ <!-- Watch out: using name argument will ignore the custom build.xml version-tag! -->
522+ </exec>
523+
524 <property file="local.properties" />
525
526- <!-- The ant.properties file can be created by you. It is only edited by the
527- 'android' tool to add properties to it.
528- This is the place to change some Ant specific build properties.
529- Here are some properties you may want to change/update:
530-
531- source.dir
532- The name of the source directory. Default is 'src'.
533- out.dir
534- The name of the output directory. Default is 'bin'.
535-
536- For other overridable properties, look at the beginning of the rules
537- files in the SDK, at tools/ant/build.xml
538-
539- Properties related to the SDK location or the project target should
540- be updated using the 'android' tool with the 'update' action.
541-
542- This file is an integral part of the build system for your
543- application and should be checked into Version Control Systems.
544-
545- -->
546 <property file="ant.properties" />
547
548 <!-- if sdk.dir was not set from one of the property file, then
549@@ -37,17 +46,6 @@
550 <isset property="env.ANDROID_HOME" />
551 </condition>
552
553- <!-- The project.properties file is created and updated by the 'android'
554- tool, as well as ADT.
555-
556- This contains project specific properties such as project target, and library
557- dependencies. Lower level build properties are stored in ant.properties
558- (or in .classpath for Eclipse projects).
559-
560- This file is an integral part of the build system for your
561- application and should be checked into Version Control Systems. -->
562- <loadproperties srcFile="project.properties" />
563-
564 <!-- quick check on sdk.dir -->
565 <fail
566 message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
567@@ -86,7 +84,7 @@
568 In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
569 in order to avoid having your file be overridden by tools such as "android update project"
570 -->
571- <!-- version-tag: 1 -->
572+ <!-- version-tag: custom -->
573 <import file="${sdk.dir}/tools/ant/build.xml" />
574
575 </project>
576
577=== added file 'check-sdk.sh'
578--- check-sdk.sh 1970-01-01 00:00:00 +0000
579+++ check-sdk.sh 2014-06-09 11:59:50 +0000
580@@ -0,0 +1,10 @@
581+#!/bin/bash
582+
583+ANDROIDBIN=$(which android)
584+if test -z "${ANDROIDBIN}"; then
585+ echo "Android SDK tools not in PATH, fix with:" >&2
586+ echo " PATH=\$PATH:/PathToSDK/tools" >&2
587+ exit 1
588+fi
589+
590+exit 0
591
592=== removed file 'make_release.sh'
593--- make_release.sh 2013-12-20 03:39:53 +0000
594+++ make_release.sh 1970-01-01 00:00:00 +0000
595@@ -1,16 +0,0 @@
596- #!/sbin/sh
597- KEY=$1
598- if [[ -z "$KEY" ]]; then
599- echo "Pass path to the keystore"
600- exit -1
601- fi
602- echo "Release tool, using key"
603- echo "Getting ready for build"
604- android update project --path .
605- echo "Removing old release"
606- rm -rf bin/
607- ant release
608- echo "Signing build"
609- jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore $KEY bin/MainActivity-release-unsigned.apk humpolec
610- mv bin/MainActivity-release-unsigned.apk bin/UbuntuInstaller.apk
611- echo "Signed relase is at bin/UbuntuInstaller.apk"
612\ No newline at end of file
613
614=== modified file 'project.properties'
615--- project.properties 2013-12-12 02:02:35 +0000
616+++ project.properties 2014-06-09 11:59:50 +0000
617@@ -11,4 +11,4 @@
618 #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
619
620 # Project target.
621-target=Google Inc.:Google APIs:19
622+target=android-19
623
624=== added file 'res/drawable-hdpi/ic_action_discard.png'
625Binary files res/drawable-hdpi/ic_action_discard.png 1970-01-01 00:00:00 +0000 and res/drawable-hdpi/ic_action_discard.png 2014-06-09 11:59:50 +0000 differ
626=== added file 'res/drawable-hdpi/ic_action_refresh.png'
627Binary files res/drawable-hdpi/ic_action_refresh.png 1970-01-01 00:00:00 +0000 and res/drawable-hdpi/ic_action_refresh.png 2014-06-09 11:59:50 +0000 differ
628=== added file 'res/drawable-hdpi/ic_action_share.png'
629Binary files res/drawable-hdpi/ic_action_share.png 1970-01-01 00:00:00 +0000 and res/drawable-hdpi/ic_action_share.png 2014-06-09 11:59:50 +0000 differ
630=== added directory 'res/drawable-ldpi'
631=== added file 'res/drawable-mdpi/ic_action_discard.png'
632Binary files res/drawable-mdpi/ic_action_discard.png 1970-01-01 00:00:00 +0000 and res/drawable-mdpi/ic_action_discard.png 2014-06-09 11:59:50 +0000 differ
633=== added file 'res/drawable-mdpi/ic_action_refresh.png'
634Binary files res/drawable-mdpi/ic_action_refresh.png 1970-01-01 00:00:00 +0000 and res/drawable-mdpi/ic_action_refresh.png 2014-06-09 11:59:50 +0000 differ
635=== added file 'res/drawable-mdpi/ic_action_share.png'
636Binary files res/drawable-mdpi/ic_action_share.png 1970-01-01 00:00:00 +0000 and res/drawable-mdpi/ic_action_share.png 2014-06-09 11:59:50 +0000 differ
637=== added file 'res/drawable-xhdpi/ic_action_discard.png'
638Binary files res/drawable-xhdpi/ic_action_discard.png 1970-01-01 00:00:00 +0000 and res/drawable-xhdpi/ic_action_discard.png 2014-06-09 11:59:50 +0000 differ
639=== added file 'res/drawable-xhdpi/ic_action_refresh.png'
640Binary files res/drawable-xhdpi/ic_action_refresh.png 1970-01-01 00:00:00 +0000 and res/drawable-xhdpi/ic_action_refresh.png 2014-06-09 11:59:50 +0000 differ
641=== added file 'res/drawable-xhdpi/ic_action_share.png'
642Binary files res/drawable-xhdpi/ic_action_share.png 1970-01-01 00:00:00 +0000 and res/drawable-xhdpi/ic_action_share.png 2014-06-09 11:59:50 +0000 differ
643=== added file 'res/drawable-xhdpi/ic_stat_notify_ubuntu.png'
644Binary files res/drawable-xhdpi/ic_stat_notify_ubuntu.png 1970-01-01 00:00:00 +0000 and res/drawable-xhdpi/ic_stat_notify_ubuntu.png 2014-06-09 11:59:50 +0000 differ
645=== added file 'res/drawable-xxhdpi/ic_action_discard.png'
646Binary files res/drawable-xxhdpi/ic_action_discard.png 1970-01-01 00:00:00 +0000 and res/drawable-xxhdpi/ic_action_discard.png 2014-06-09 11:59:50 +0000 differ
647=== added file 'res/drawable-xxhdpi/ic_action_refresh.png'
648Binary files res/drawable-xxhdpi/ic_action_refresh.png 1970-01-01 00:00:00 +0000 and res/drawable-xxhdpi/ic_action_refresh.png 2014-06-09 11:59:50 +0000 differ
649=== added file 'res/drawable-xxhdpi/ic_action_share.png'
650Binary files res/drawable-xxhdpi/ic_action_share.png 1970-01-01 00:00:00 +0000 and res/drawable-xxhdpi/ic_action_share.png 2014-06-09 11:59:50 +0000 differ
651=== modified file 'res/layout/ubuntu_dualboot_launch.xml'
652--- res/layout/ubuntu_dualboot_launch.xml 2013-12-12 02:02:35 +0000
653+++ res/layout/ubuntu_dualboot_launch.xml 2014-06-09 11:59:50 +0000
654@@ -10,18 +10,6 @@
655 android:background="#FFFFFF"
656 android:orientation="vertical" >
657
658- <com.canonical.ubuntu.widget.UbuntuButton
659- android:id="@+id/download"
660- style="@style/Button.Grey"
661- android:layout_width="match_parent"
662- android:layout_height="wrap_content"
663- android:layout_alignParentBottom="true"
664- android:layout_gravity="center_horizontal"
665- android:adjustViewBounds="true"
666- android:text="hi"
667- android:textColor="@color/text"
668- u1f:customFont="Ubuntu-R.ttf" />
669-
670 <com.canonical.ubuntu.widget.UbuntuTextView
671 android:id="@+id/title"
672 android:layout_width="wrap_content"
673@@ -54,7 +42,7 @@
674 android:layout_alignParentLeft="true"
675 android:layout_centerHorizontal="true"
676 android:layout_margin="2dp"
677- android:text="Info:"
678+ android:text="Build number:"
679 android:textAppearance="?android:attr/textAppearanceMedium"
680 android:textColor="@color/ubuntuorange"
681 android:textStyle="bold"
682@@ -84,7 +72,7 @@
683 android:layout_alignParentLeft="true"
684 android:layout_centerHorizontal="true"
685 android:layout_margin="2dp"
686- android:text="Info:"
687+ android:text="Channel:"
688 android:textAppearance="?android:attr/textAppearanceMedium"
689 android:textColor="@color/ubuntuorange"
690 android:textStyle="bold"
691@@ -97,7 +85,7 @@
692 android:layout_margin="2dp"
693 android:layout_toRightOf="@id/channel_label"
694 android:singleLine="true"
695- android:text="add version"
696+ android:text="add channel"
697 android:textAppearance="?android:attr/textAppearanceMedium"
698 android:textColor="@color/text"
699 u1f:customFont="Ubuntu-R.ttf" />
700@@ -108,13 +96,43 @@
701 android:layout_height="wrap_content" >
702
703 <com.canonical.ubuntu.widget.UbuntuTextView
704+ android:id="@+id/ref_channel_label"
705+ android:layout_width="wrap_content"
706+ android:layout_height="wrap_content"
707+ android:layout_alignParentLeft="true"
708+ android:layout_centerHorizontal="true"
709+ android:layout_margin="2dp"
710+ android:text="Target Channel:"
711+ android:textAppearance="?android:attr/textAppearanceMedium"
712+ android:textColor="@color/ubuntuorange"
713+ android:textStyle="bold"
714+ u1f:customFont="Ubuntu-R.ttf" />
715+
716+ <com.canonical.ubuntu.widget.UbuntuTextView
717+ android:id="@+id/ref_channel"
718+ android:layout_width="wrap_content"
719+ android:layout_height="wrap_content"
720+ android:layout_margin="2dp"
721+ android:layout_toRightOf="@id/ref_channel_label"
722+ android:singleLine="true"
723+ android:text="add channel"
724+ android:textAppearance="?android:attr/textAppearanceMedium"
725+ android:textColor="@color/text"
726+ u1f:customFont="Ubuntu-R.ttf" />
727+ </RelativeLayout>
728+
729+ <RelativeLayout
730+ android:layout_width="wrap_content"
731+ android:layout_height="wrap_content" >
732+
733+ <com.canonical.ubuntu.widget.UbuntuTextView
734 android:id="@+id/description_label"
735 android:layout_width="wrap_content"
736 android:layout_height="wrap_content"
737 android:layout_alignParentLeft="true"
738 android:layout_centerHorizontal="true"
739 android:layout_margin="2dp"
740- android:text="Info:"
741+ android:text="Description:"
742 android:textAppearance="?android:attr/textAppearanceMedium"
743 android:textColor="@color/ubuntuorange"
744 android:textStyle="bold"
745@@ -125,43 +143,14 @@
746 android:layout_width="wrap_content"
747 android:layout_height="wrap_content"
748 android:layout_margin="2dp"
749- android:layout_toRightOf="@id/description_label"
750- android:singleLine="true"
751- android:text="add version"
752- android:textAppearance="?android:attr/textAppearanceMedium"
753- android:textColor="@color/text"
754- u1f:customFont="Ubuntu-R.ttf" />
755- </RelativeLayout>
756-
757- <RelativeLayout
758- android:layout_width="wrap_content"
759- android:layout_height="wrap_content" >
760-
761- <com.canonical.ubuntu.widget.UbuntuTextView
762- android:id="@+id/total_label"
763- android:layout_width="wrap_content"
764- android:layout_height="wrap_content"
765- android:layout_alignParentLeft="true"
766- android:layout_centerHorizontal="true"
767- android:layout_margin="2dp"
768- android:text="Info:"
769- android:textAppearance="?android:attr/textAppearanceMedium"
770- android:textColor="@color/ubuntuorange"
771- android:textStyle="bold"
772- u1f:customFont="Ubuntu-R.ttf" />
773-
774- <com.canonical.ubuntu.widget.UbuntuTextView
775- android:id="@+id/total"
776- android:layout_width="wrap_content"
777- android:layout_height="wrap_content"
778- android:layout_margin="2dp"
779- android:layout_toRightOf="@id/total_label"
780- android:singleLine="true"
781- android:text="add version"
782- android:textAppearance="?android:attr/textAppearanceMedium"
783- android:textColor="@color/text"
784- u1f:customFont="Ubuntu-R.ttf" />
785- </RelativeLayout>
786+ android:layout_below="@id/description_label"
787+ android:singleLine="true"
788+ android:text="add description"
789+ android:textAppearance="?android:attr/textAppearanceMedium"
790+ android:textColor="@color/text"
791+ u1f:customFont="Ubuntu-R.ttf" />
792+ </RelativeLayout>
793+
794
795 <View
796 android:id="@+id/divider"
797@@ -175,61 +164,120 @@
798 android:layout_marginLeft="10dp" >
799
800 <com.canonical.ubuntu.widget.UbuntuTextView
801- android:id="@+id/app_label"
802- android:layout_width="wrap_content"
803- android:layout_height="wrap_content"
804- android:layout_alignParentLeft="true"
805- android:layout_centerHorizontal="true"
806- android:layout_margin="2dp"
807- android:text="Info:"
808- android:textAppearance="?android:attr/textAppearanceMedium"
809- android:textColor="@color/text"
810- android:textStyle="bold"
811- u1f:customFont="Ubuntu-R.ttf" />
812-
813- <com.canonical.ubuntu.widget.UbuntuTextView
814- android:id="@+id/appsize"
815- android:layout_width="wrap_content"
816- android:layout_height="wrap_content"
817- android:layout_margin="2dp"
818- android:layout_toRightOf="@id/app_label"
819- android:singleLine="true"
820- android:text="add version"
821- android:textAppearance="?android:attr/textAppearanceMedium"
822- android:textColor="@color/text"
823- u1f:customFont="Ubuntu-R.ttf" />
824- </RelativeLayout>
825-
826- <RelativeLayout
827- android:layout_width="wrap_content"
828- android:layout_height="wrap_content"
829- android:layout_marginLeft="10dp" >
830-
831- <com.canonical.ubuntu.widget.UbuntuTextView
832- android:id="@+id/data_label"
833- android:layout_width="wrap_content"
834- android:layout_height="wrap_content"
835- android:layout_alignParentLeft="true"
836- android:layout_centerHorizontal="true"
837- android:layout_margin="2dp"
838- android:text="Info:"
839- android:textAppearance="?android:attr/textAppearanceMedium"
840- android:textColor="@color/text"
841- android:textStyle="bold"
842- u1f:customFont="Ubuntu-R.ttf" />
843-
844- <com.canonical.ubuntu.widget.UbuntuTextView
845- android:id="@+id/datasize"
846- android:layout_width="wrap_content"
847- android:layout_height="wrap_content"
848- android:layout_margin="2dp"
849- android:layout_toRightOf="@id/data_label"
850- android:singleLine="true"
851- android:text="add version"
852+ android:id="@+id/rootfs_label"
853+ android:layout_width="wrap_content"
854+ android:layout_height="wrap_content"
855+ android:layout_alignParentLeft="true"
856+ android:layout_centerHorizontal="true"
857+ android:layout_margin="2dp"
858+ android:text="rootfs:"
859+ android:textAppearance="?android:attr/textAppearanceMedium"
860+ android:textColor="@color/text"
861+ android:textStyle="bold"
862+ u1f:customFont="Ubuntu-R.ttf" />
863+
864+ <com.canonical.ubuntu.widget.UbuntuTextView
865+ android:id="@+id/rootfssize"
866+ android:layout_width="wrap_content"
867+ android:layout_height="wrap_content"
868+ android:layout_margin="2dp"
869+ android:layout_toRightOf="@id/rootfs_label"
870+ android:singleLine="true"
871+ android:text="used MB"
872+ android:textAppearance="?android:attr/textAppearanceMedium"
873+ android:textColor="@color/text"
874+ u1f:customFont="Ubuntu-R.ttf" />
875+ </RelativeLayout>
876+
877+
878+
879+ <RelativeLayout
880+ android:layout_width="wrap_content"
881+ android:layout_height="wrap_content"
882+ android:layout_marginLeft="10dp" >
883+
884+ <com.canonical.ubuntu.widget.UbuntuTextView
885+ android:id="@+id/system_data_label"
886+ android:layout_width="wrap_content"
887+ android:layout_height="wrap_content"
888+ android:layout_alignParentLeft="true"
889+ android:layout_centerHorizontal="true"
890+ android:layout_margin="2dp"
891+ android:text="System-data:"
892+ android:textAppearance="?android:attr/textAppearanceMedium"
893+ android:textColor="@color/text"
894+ android:textStyle="bold"
895+ u1f:customFont="Ubuntu-R.ttf" />
896+
897+ <com.canonical.ubuntu.widget.UbuntuTextView
898+ android:id="@+id/system_data_size"
899+ android:layout_width="wrap_content"
900+ android:layout_height="wrap_content"
901+ android:layout_margin="2dp"
902+ android:layout_toRightOf="@id/system_data_label"
903+ android:singleLine="true"
904+ android:text="used MB"
905+ android:textAppearance="?android:attr/textAppearanceMedium"
906+ android:textColor="@color/text"
907+ u1f:customFont="Ubuntu-R.ttf" />
908+ </RelativeLayout>
909+
910+ <RelativeLayout
911+ android:layout_width="wrap_content"
912+ android:layout_height="wrap_content"
913+ android:layout_marginLeft="10dp" >
914+
915+ <com.canonical.ubuntu.widget.UbuntuTextView
916+ android:id="@+id/user_data_label"
917+ android:layout_width="wrap_content"
918+ android:layout_height="wrap_content"
919+ android:layout_alignParentLeft="true"
920+ android:layout_centerHorizontal="true"
921+ android:layout_margin="2dp"
922+ android:text="User-data:"
923+ android:textAppearance="?android:attr/textAppearanceMedium"
924+ android:textColor="@color/text"
925+ android:textStyle="bold"
926+ u1f:customFont="Ubuntu-R.ttf" />
927+
928+ <com.canonical.ubuntu.widget.UbuntuTextView
929+ android:id="@+id/user_datasize"
930+ android:layout_width="wrap_content"
931+ android:layout_height="wrap_content"
932+ android:layout_margin="2dp"
933+ android:layout_toRightOf="@id/user_data_label"
934+ android:singleLine="true"
935+ android:text="used MB"
936 android:textAppearance="?android:attr/textAppearanceMedium"
937 android:textColor="@color/text"
938 u1f:customFont="Ubuntu-R.ttf" />
939 </RelativeLayout>
940 </LinearLayout>
941
942+ <com.canonical.ubuntu.widget.UbuntuButton
943+ android:id="@+id/upgrade"
944+ style="@style/Button.Grey"
945+ android:layout_width="match_parent"
946+ android:layout_height="wrap_content"
947+ android:layout_centerHorizontal="true"
948+ android:layout_marginBottom="14dp"
949+ android:layout_above="@+id/reboot"
950+ android:adjustViewBounds="true"
951+ android:text="Install Upgrade"
952+ android:textColor="@color/text"
953+ u1f:customFont="Ubuntu-R.ttf" />
954+
955+ <com.canonical.ubuntu.widget.UbuntuButton
956+ android:id="@+id/reboot"
957+ style="@style/Button.Grey"
958+ android:layout_width="match_parent"
959+ android:layout_height="wrap_content"
960+ android:layout_alignParentBottom="true"
961+ android:layout_centerHorizontal="true"
962+ android:layout_marginBottom="14dp"
963+ android:adjustViewBounds="true"
964+ android:text="Boot Ubuntu"
965+ android:textColor="@color/text"
966+ u1f:customFont="Ubuntu-R.ttf" />
967+
968 </RelativeLayout>
969\ No newline at end of file
970
971=== modified file 'res/menu/installer_menu.xml'
972--- res/menu/installer_menu.xml 2013-12-25 03:13:44 +0000
973+++ res/menu/installer_menu.xml 2014-06-09 11:59:50 +0000
974@@ -1,19 +1,15 @@
975 <menu xmlns:android="http://schemas.android.com/apk/res/android" >
976
977 <item
978- android:id="@+id/action_delete_download"
979+ android:id="@+id/menu_action_delete_download"
980 android:orderInCategory="100"
981- android:showAsAction="never"
982+ android:showAsAction="ifRoom"
983+ android:icon="@drawable/ic_action_discard"
984 android:title="@string/action_detele_download"/>
985 <item
986- android:id="@+id/action_dump_terminal"
987+ android:id="@+id/menu_action_dump_terminal"
988 android:orderInCategory="100"
989- android:showAsAction="never"
990+ android:showAsAction="ifRoom"
991+ android:icon="@drawable/ic_action_share"
992 android:title="@string/action_dump_terminal"/>
993-<!-- <item
994- android:id="@+id/action_settings"
995- android:orderInCategory="100"
996- android:showAsAction="never"
997- android:title="@string/action_settings"/>
998- -->
999 </menu>
1000
1001=== modified file 'res/menu/launcher_menu.xml'
1002--- res/menu/launcher_menu.xml 2013-12-20 03:39:53 +0000
1003+++ res/menu/launcher_menu.xml 2014-06-09 11:59:50 +0000
1004@@ -1,20 +1,29 @@
1005 <menu xmlns:android="http://schemas.android.com/apk/res/android" >
1006-
1007- <item
1008- android:id="@+id/action_uninstall"
1009+ <item
1010+ android:id="@+id/menu_action_update_android"
1011+ android:orderInCategory="100"
1012+ android:showAsAction="never"
1013+ android:title="@string/android_update_menu_option"/>
1014+ <item
1015+ android:id="@+id/menu_action_uninstall"
1016 android:orderInCategory="100"
1017 android:showAsAction="never"
1018 android:title="@string/action_uninstall"/>
1019 <item
1020- android:id="@+id/action_del_user_data"
1021+ android:id="@+id/menu_action_del_user_data"
1022 android:orderInCategory="100"
1023- android:showAsAction="never"
1024+ android:showAsAction="ifRoom"
1025+ android:icon="@drawable/ic_action_discard"
1026 android:title="@string/action_delete_user_data"/>
1027-
1028-<!-- <item
1029- android:id="@+id/action_settings"
1030+ <item
1031+ android:id="@+id/menu_action_update_storage_use"
1032 android:orderInCategory="100"
1033 android:showAsAction="never"
1034- android:title="@string/action_settings"/>
1035--->
1036+ android:title="@string/action_update_storage_use"/>
1037+ <item
1038+ android:id="@+id/menu_action_check_for_update"
1039+ android:orderInCategory="100"
1040+ android:showAsAction="ifRoom"
1041+ android:icon="@drawable/ic_action_refresh"
1042+ android:title="@string/action_check_for_update"/>
1043 </menu>
1044
1045=== modified file 'res/values/attrs.xml'
1046--- res/values/attrs.xml 2013-12-10 14:26:08 +0000
1047+++ res/values/attrs.xml 2014-06-09 11:59:50 +0000
1048@@ -1,20 +1,20 @@
1049 <?xml version="1.0" encoding="utf-8"?>
1050 <!--
1051 /*
1052- * This file is part of Ubuntu for Android.
1053+ * This file is part of Ubuntu dualboot installer for Android.
1054 * Copyright 2013 Canonical Ltd.
1055 *
1056- * Ubuntu for Android is free software: you can redistribute it and/or modify
1057+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
1058 * it under the terms of the GNU General Public License as published by
1059 * the Free Software Foundation.
1060 *
1061- * Ubuntu for Android is distributed in the hope that it will be useful,
1062+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
1063 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1064 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
1065 * See the GNU General Public License for more details.
1066 *
1067 * You should have received a copy of the GNU General Public License
1068- * along with Ubuntu for Android. If not, see <http://www.gnu.org/licenses/>.
1069+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
1070 */
1071 -->
1072
1073
1074=== modified file 'res/values/colors.xml'
1075--- res/values/colors.xml 2013-12-10 14:26:08 +0000
1076+++ res/values/colors.xml 2014-06-09 11:59:50 +0000
1077@@ -1,20 +1,20 @@
1078 <?xml version="1.0" encoding="utf-8"?>
1079 <!--
1080 /*
1081- * This file is part of Ubuntu for Android.
1082+ * This file is part of Ubuntu dualboot installer for Android.
1083 * Copyright 2013 Canonical Ltd.
1084 *
1085- * Ubuntu for Android is free software: you can redistribute it and/or modify
1086+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
1087 * it under the terms of the GNU General Public License as published by
1088 * the Free Software Foundation.
1089 *
1090- * Ubuntu for Android is distributed in the hope that it will be useful,
1091+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
1092 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1093 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
1094 * See the GNU General Public License for more details.
1095 *
1096 * You should have received a copy of the GNU General Public License
1097- * along with Ubuntu for Android. If not, see <http://www.gnu.org/licenses/>.
1098+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
1099 */
1100 -->
1101
1102
1103=== added file 'res/values/ids.xml'
1104--- res/values/ids.xml 1970-01-01 00:00:00 +0000
1105+++ res/values/ids.xml 2014-06-09 11:59:50 +0000
1106@@ -0,0 +1,26 @@
1107+<?xml version="1.0" encoding="utf-8"?>
1108+<!--
1109+/*
1110+ * This file is part of Ubuntu dualboot installer for Android.
1111+ * Copyright 2013 Canonical Ltd.
1112+ *
1113+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
1114+ * it under the terms of the GNU General Public License as published by
1115+ * the Free Software Foundation.
1116+ *
1117+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
1118+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1119+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
1120+ * See the GNU General Public License for more details.
1121+ *
1122+ * You should have received a copy of the GNU General Public License
1123+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
1124+ */
1125+-->
1126+
1127+<resources>
1128+ <item type="id" name="ubuntu_notification" />
1129+ <item type="id" name="ubuntu_progress_notification" />
1130+ <item type="id" name="ubuntu_update_notification" />
1131+ <item type="id" name="android_update_notification" />
1132+</resources>
1133\ No newline at end of file
1134
1135=== modified file 'res/values/strings.xml'
1136--- res/values/strings.xml 2014-01-09 08:54:46 +0000
1137+++ res/values/strings.xml 2014-06-09 11:59:50 +0000
1138@@ -1,4 +1,23 @@
1139 <?xml version="1.0" encoding="utf-8"?>
1140+<!--
1141+/*
1142+ * This file is part of Ubuntu dualboot installer for Android.
1143+ * Copyright 2013 Canonical Ltd.
1144+ *
1145+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
1146+ * it under the terms of the GNU General Public License as published by
1147+ * the Free Software Foundation.
1148+ *
1149+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
1150+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1151+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
1152+ * See the GNU General Public License for more details.
1153+ *
1154+ * You should have received a copy of the GNU General Public License
1155+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
1156+ */
1157+-->
1158+
1159 <resources>
1160 <string name="yes">Yes</string>
1161 <string name="no">No</string>
1162@@ -11,50 +30,87 @@
1163 <string name="action_dump_terminal">Dump Terminal</string>
1164 <string name="action_install">Install</string>
1165 <string name="action_uninstall">Uninstall Ubuntu</string>
1166+
1167 <string name="action_uninstall_button">Uninstall</string>
1168- <string name="action_delete_user_data">Delete Ubuntu user data</string>
1169+ <string name="action_uninstall_ubuntu">Uninstall Ubuntu</string>
1170+ <string name="action_delete_user_data">Delete Ubuntu data</string>
1171+ <string name="action_update_storage_use">Update storage use</string>
1172+ <string name="action_check_for_update">Check for update</string>
1173 <string name="action_delete_udata_button">Delete</string>
1174 <string name="cancel">Cancel</string>
1175- <string name="welcome_world">Your life is about to change, welcome in Ubuntu installer</string>
1176- <string name="install_button_label_install">Choose channel to install</string>
1177+ <string name="welcome_world">Your life is about to change, welcome to the Ubuntu installer</string>
1178+ <string name="install_button_label_install">Choose which channel to install</string>
1179 <string name="install_button_label_fetching"><![CDATA[Install Ubuntu<br/><small>fetching channel list</small>]]></string>
1180 <string name="install_button_label_no_channel"><![CDATA[Install Ubuntu<br/><small>no available channels</small>]]></string>
1181 <string name="install_button_label_resume">Resume install</string>
1182- <string name="install_button_label_cancel_download">Cancel download</string>
1183- <string name="install_button_label_cancel_install">Cancel install</string>
1184- <string name="channel_picker_dialog_title">Select channel to install</string>
1185- <string name="version_picker_dialog_title">Select version to install</string>
1186+ <string name="install_button_label_resume_download">Resume download</string>
1187+ <string name="install_button_label_pause_download">Pause download</string>
1188+ <string name="install_button_label_installing">Installing....</string>
1189+ <string name="channel_picker_dialog_title">Select which channel to install</string>
1190+ <string name="version_picker_dialog_title">Select which version to install</string>
1191 <string name="bootstrap_check">bootstrap</string>
1192 <string name="latest_check">latest version</string>
1193 <string name="downloading_release">Downloading Ubuntu</string>
1194- <string name="downloading_index">Downloading images data</string>
1195+ <string name="downloading_index">Downloading image data</string>
1196 <string name="downloading_starting">Starting download</string>
1197 <string name="installing_release">Installing Ubuntu</string>
1198 <string name="uninstall_dialog_title">Uninstall Ubuntu?</string>
1199+ <string name="uninstall_dialog_description">Ubuntu installation will be removed and resources freed. If not specified Ubuntu user data will not be deleted</string>
1200 <string name="uninstalling_ubuntu">Uninstalling Ubuntu</string>
1201 <string name="deleting_user_data">Deleting Ubuntu user data</string>
1202+ <string name="deleting_download">Deleting download</string>
1203 <string name="fail_fetching_versions">Fail to fetch available versions</string>
1204- <string name="uninstall_dialog_message">This will remove all Ubuntu system files and Ubuntu swap file</string>
1205- <string name="terminal_is_empty">Terminal is emtpy.</string>
1206- <string name="terminal_dump_succ">Dump Terminal successfully to file on sdcard</string>
1207- <string name="terminal_dump_fail">Dump Terminal failed to file on sdcard</string>
1208+ <string name="uninstall_dialog_message">This will remove all Ubuntu system files and the Ubuntu swap file</string>
1209+ <string name="terminal_is_empty">Terminal is empty.</string>
1210+ <string name="terminal_dump_succ">Dump Terminal to file on sdcard successful</string>
1211+ <string name="terminal_dump_fail">Dump Terminal to file on sdcard failed</string>
1212 <string name="external_storage_unavailable">External Storage is not available</string>
1213
1214 <string name="not_supported_title">Not supported</string>
1215- <string name="not_supported_message_fmt">It appears that your device (%s) is not on the supported list of Ubuntu for Phone dual boot installer. Visit <a href="https://wiki.ubuntu.com/Touch/DualBootInstallation">https://wiki.ubuntu.com/Touch/DualBootInstallation</a> to see phones that is supported.</string>
1216+ <string name="not_supported_message_fmt">It appears that your device (%s) is not supported by the Ubuntu for Phone dual boot installer. Visit <a href="https://wiki.ubuntu.com/Touch/DualBootInstallation">https://wiki.ubuntu.com/Touch/DualBootInstallation</a> to see which phones are supported.</string>
1217
1218 <string name="no_network_dialog_title">Network not available</string>
1219 <string name="enable_network">Enable WiFi</string>
1220+ <string name="not_available">Not available</string>
1221+
1222+ <string name="android_update_dialog_title">Android system update detected</string>
1223+ <string name="android_update_dialog_possitive">Prepare for update</string>
1224+ <string name="android_update_menu_option">Prepare for Android update</string>
1225+ <string name="android_update_dialog_description">Android system update has been detected. Original recovery needs to be restored to complete system update. This will not afect existing Ubuntu installation. Once system is prepared start Settings application and complete system update. It is likely update removes SuperUser capability, requiring reinstall of superuser(dualboot application), existing Ubuntu installation will be preserved.</string>
1226
1227 <!-- Strings for LaunchActivity screen -->
1228 <string name="launch_title">Ubuntu</string>
1229 <string name="label_channel">Channel:</string>
1230+ <string name="label_channel_target">Target channel:</string>
1231 <string name="label_version">Version:</string>
1232 <string name="label_description">Description:</string>
1233+ <string name="label_rootfs">rootfs:</string>
1234+ <string name="label_system_data">system-data:</string>
1235+ <string name="label_user_data">user-data:</string>
1236 <string name="reboot_button_label">Reboot to Ubuntu</string>
1237-
1238+ <string name="button_install_ubuntu_update">Install Ubuntu update</string>
1239+ <string name="button_install_android_update">Install pending Android update</string>
1240+
1241+ <string name="notification_title">Ubuntu</string>
1242+ <string name="notification_update_available">Ubuntu update available</string>
1243+ <string name="notification_update_installed">Ubuntu update installed</string>
1244+ <string name="notification_ubuntu_installed">Ubuntu installed</string>
1245+ <string name="notification_download_failed">Ubuntu download failed</string>
1246+ <string name="notification_installation_failed">Ubuntu installation failed</string>
1247+ <string name="notification_downloading_release">Downloading Ubuntu release</string>
1248+ <string name="notification_downloading_update">Downloading Ubuntu update</string>
1249+ <string name="notification_installing_release">Installing Ubuntu release</string>
1250+ <string name="notification_installing_update">Installing Ubuntu update</string>
1251+ <string name="notification_android_update">Android update available</string>
1252+ <string name="not_enough_charge_warning">Battery level is too low to continue with installation</string>
1253+
1254 <string-array name="uninstall_options">
1255 <item>Delete user data</item>
1256 </string-array>
1257+
1258+ <string-array name="update_android_options">
1259+ <item>Never ask again</item>
1260+ </string-array>
1261+
1262 </resources>
1263
1264
1265=== modified file 'res/values/styles.xml'
1266--- res/values/styles.xml 2013-12-10 14:26:08 +0000
1267+++ res/values/styles.xml 2014-06-09 11:59:50 +0000
1268@@ -1,20 +1,20 @@
1269 <?xml version="1.0" encoding="utf-8"?>
1270 <!--
1271 /*
1272- * This file is part of Ubuntu for Android.
1273+ * This file is part of Ubuntu dualboot installer for Android.
1274 * Copyright 2013 Canonical Ltd.
1275 *
1276- * Ubuntu for Android is free software: you can redistribute it and/or modify
1277+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
1278 * it under the terms of the GNU General Public License as published by
1279 * the Free Software Foundation.
1280 *
1281- * Ubuntu for Android is distributed in the hope that it will be useful,
1282+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
1283 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1284 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
1285 * See the GNU General Public License for more details.
1286 *
1287 * You should have received a copy of the GNU General Public License
1288- * along with Ubuntu for Android. If not, see <http://www.gnu.org/licenses/>.
1289+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
1290 */
1291 -->
1292
1293
1294=== added file 'src/com/canonical/ubuntu/installer/BaseActivity.java'
1295--- src/com/canonical/ubuntu/installer/BaseActivity.java 1970-01-01 00:00:00 +0000
1296+++ src/com/canonical/ubuntu/installer/BaseActivity.java 2014-06-09 11:59:50 +0000
1297@@ -0,0 +1,70 @@
1298+/*
1299+ * This file is part of Ubuntu dualboot installer for Android.
1300+ * Copyright 2013 Canonical Ltd.
1301+ *
1302+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
1303+ * it under the terms of the GNU General Public License as published by
1304+ * the Free Software Foundation.
1305+ *
1306+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
1307+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1308+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
1309+ * See the GNU General Public License for more details.
1310+ *
1311+ * You should have received a copy of the GNU General Public License
1312+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
1313+ */
1314+
1315+package com.canonical.ubuntu.installer;
1316+
1317+import android.app.Activity;
1318+import android.content.ComponentName;
1319+import android.content.Context;
1320+import android.content.Intent;
1321+import android.content.ServiceConnection;
1322+import android.os.IBinder;
1323+
1324+public class BaseActivity extends Activity {
1325+ public UbuntuInstallService mInstallerService;
1326+ private boolean mServiceIsBound;
1327+
1328+ public BaseActivity() {
1329+ }
1330+
1331+ private ServiceConnection mConnection = new ServiceConnection() {
1332+ public void onServiceConnected(ComponentName className, IBinder service) {
1333+ mInstallerService = ((UbuntuInstallService.LocalBinder)service).getService();
1334+ onInstallerServiceConnected();
1335+ }
1336+
1337+ public void onServiceDisconnected(ComponentName className) {
1338+ mInstallerService = null;
1339+ onInstallerServiceDisconnected();
1340+ }
1341+ };
1342+
1343+ void doBindService() {
1344+ bindService(new Intent(this, UbuntuInstallService.class), mConnection, Context.BIND_AUTO_CREATE);
1345+ mServiceIsBound = true;
1346+ }
1347+
1348+ void doUnbindService() {
1349+ if (mServiceIsBound) {
1350+ // Detach our existing connection.
1351+ unbindService(mConnection);
1352+ mServiceIsBound = false;
1353+ }
1354+ }
1355+
1356+ /**
1357+ * Overwrite this function to handle service connected
1358+ */
1359+ protected void onInstallerServiceConnected() {
1360+ }
1361+
1362+ /**
1363+ * Overwrite this function to handle service disconnected
1364+ */
1365+ protected void onInstallerServiceDisconnected() {
1366+ }
1367+}
1368
1369=== added file 'src/com/canonical/ubuntu/installer/BootReceiver.java'
1370--- src/com/canonical/ubuntu/installer/BootReceiver.java 1970-01-01 00:00:00 +0000
1371+++ src/com/canonical/ubuntu/installer/BootReceiver.java 2014-06-09 11:59:50 +0000
1372@@ -0,0 +1,39 @@
1373+/*
1374+ * This file is part of Ubuntu dualboot installer for Android.
1375+ * Copyright 2013 Canonical Ltd.
1376+ *
1377+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
1378+ * it under the terms of the GNU General Public License as published by
1379+ * the Free Software Foundation.
1380+ *
1381+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
1382+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1383+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
1384+ * See the GNU General Public License for more details.
1385+ *
1386+ * You should have received a copy of the GNU General Public License
1387+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
1388+ */
1389+
1390+package com.canonical.ubuntu.installer;
1391+
1392+import android.content.BroadcastReceiver;
1393+import android.content.Context;
1394+import android.content.Intent;
1395+import android.content.SharedPreferences;
1396+import android.util.Log;
1397+
1398+public class BootReceiver extends BroadcastReceiver {
1399+ private static final String TAG = "UbuntuBootReceiver";
1400+
1401+ @Override
1402+ public void onReceive(Context context, Intent intent) {
1403+ // check if there is any Ubuntu update pending
1404+ SharedPreferences sp = context.getSharedPreferences( UbuntuInstallService.SHARED_PREF, Context.MODE_PRIVATE);
1405+ if (sp.getBoolean(UbuntuInstallService.PREF_KEY_UBUNTU_INSTALLED, false)) {
1406+ // register alarm for periodical update checks
1407+ Log.d(TAG, "Setting up alarm for update checks");
1408+ Utils.registerUpdateCheckAlarm(context, sp);
1409+ }
1410+ }
1411+}
1412
1413=== modified file 'src/com/canonical/ubuntu/installer/InstallActivity.java'
1414--- src/com/canonical/ubuntu/installer/InstallActivity.java 2014-01-09 08:54:46 +0000
1415+++ src/com/canonical/ubuntu/installer/InstallActivity.java 2014-06-09 11:59:50 +0000
1416@@ -1,3 +1,20 @@
1417+/*
1418+ * This file is part of Ubuntu dualboot installer for Android.
1419+ * Copyright 2013 Canonical Ltd.
1420+ *
1421+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
1422+ * it under the terms of the GNU General Public License as published by
1423+ * the Free Software Foundation.
1424+ *
1425+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
1426+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1427+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
1428+ * See the GNU General Public License for more details.
1429+ *
1430+ * You should have received a copy of the GNU General Public License
1431+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
1432+ */
1433+
1434 package com.canonical.ubuntu.installer;
1435
1436 import java.io.File;
1437@@ -13,7 +30,7 @@
1438 import com.canonical.ubuntu.installer.UbuntuInstallService;
1439 import com.canonical.ubuntu.installer.JsonChannelParser.Image;
1440 import com.canonical.ubuntu.installer.UbuntuInstallService.InstallerState;
1441-import com.canonical.ubuntu.installer.VersionInfo.ReleaseType;
1442+import com.canonical.ubuntu.installer.UbuntuInstallService.ReleaseType;
1443 import com.canonical.ubuntu.widget.UbuntuButton;
1444
1445 import android.net.ConnectivityManager;
1446@@ -22,7 +39,6 @@
1447 import android.os.Build;
1448 import android.os.Bundle;
1449 import android.os.Environment;
1450-import android.app.Activity;
1451 import android.app.AlertDialog;
1452 import android.app.Dialog;
1453 import android.app.DialogFragment;
1454@@ -44,36 +60,53 @@
1455 import android.view.MenuItem;
1456 import android.view.View;
1457 import android.view.View.OnClickListener;
1458+import android.view.ViewTreeObserver;
1459 import android.widget.ProgressBar;
1460 import android.widget.TextView;
1461
1462-public class InstallActivity extends Activity {
1463+public class InstallActivity extends BaseActivity {
1464 private static final String TAG = "UbuntuInstaller";
1465
1466 private UbuntuButton mInstallButton;
1467- private boolean mObserversRegistered;
1468-
1469- private InstallerState mStatus = InstallerState.READY;
1470- private VersionInfo mDownloadedVersion = null;
1471-
1472 private ProgressBar mProgressBar;
1473- private TextView mProgressText;
1474-
1475+ private TextView mProgressText;
1476 private TextView mTerminal;
1477- private HashMap<String, String> mAvailableChannels = new HashMap<String, String>();
1478+ private ViewTreeObserver.OnGlobalLayoutListener mTerminalLayoutObserver;
1479+
1480+ private String mServerUrl;
1481+ private boolean mObserversRegistered;
1482+ private HashMap<String, String> mAvailableChannels = null;
1483+ private SharedPreferences mSp;
1484
1485 @Override
1486 protected void onCreate(Bundle savedInstanceState) {
1487 super.onCreate(savedInstanceState);
1488+ doBindService();
1489 if (! Utils.isDeviceSupported()) {
1490 Log.d(TAG, "Device is not supported: " + Build.DEVICE);
1491 showNotCompatibleDialog();
1492 }
1493-
1494+ mSp = getSharedPreferences(UbuntuInstallService.SHARED_PREF, Context.MODE_PRIVATE);
1495+ // Pick default server URL
1496+ mServerUrl = Utils.getReleaseServerUrl(this);
1497+
1498 // check is there is already Ubuntu installed
1499- checkIfUbuntuIsInstalled();
1500 if (isFinishing()) return;
1501
1502+ Intent i = getIntent();
1503+ String updateCommand = i.getStringExtra("updateCommand");
1504+ if (updateCommand != null) {
1505+ Log.e(TAG,"Started with upadteCommand:" + updateCommand);
1506+ File updateCommandFile = new File(updateCommand);
1507+ if (updateCommandFile.exists()) {
1508+ mSp.edit()
1509+ .putString(UbuntuInstallService.PREF_KEY_UPDATE_COMMAND, updateCommand)
1510+ .putInt(UbuntuInstallService.PREF_KEY_DOWNLOAD_TYPE, UbuntuInstallService.ReleaseType.FULL.getValue())
1511+ .commit();
1512+ Utils.parseAndUpdateInstallCommand(this, updateCommandFile);
1513+ }
1514+ }
1515+
1516 setContentView(R.layout.ubuntu_dualboot_install);
1517 mInstallButton = (UbuntuButton) findViewById(R.id.download);
1518 mProgressBar = (ProgressBar) findViewById(R.id.progress);
1519@@ -82,44 +115,55 @@
1520 mTerminal.setMovementMethod(new ScrollingMovementMethod());
1521 mProgressBar.setEnabled(false);
1522 mProgressBar.setProgress(0);
1523-
1524- SharedPreferences pref = getSharedPreferences(UbuntuInstallService.SHARED_PREF, Context.MODE_PRIVATE);
1525- pref.edit().putBoolean(UbuntuInstallService.PREF_KEY_DEVELOPER, true).commit();
1526- }
1527-
1528+ mTerminalLayoutObserver = new ViewTreeObserver.OnGlobalLayoutListener() {
1529+ @Override
1530+ public void onGlobalLayout() {
1531+ // update scrolling
1532+ final int scrollAmount = mTerminal.getLayout().getLineTop(mTerminal.getLineCount()) - mTerminal.getHeight();
1533+ mTerminal.scrollTo(0, scrollAmount);
1534+
1535+ }
1536+ };
1537+ mSp.edit().putBoolean(UbuntuInstallService.PREF_KEY_DEVELOPER, true).commit();
1538+ }
1539+ @Override
1540+ protected void onDestroy() {
1541+ super.onDestroy();
1542+ doUnbindService();
1543+ }
1544+
1545 @Override
1546 public void onResume() {
1547 super.onResume();
1548 // check is there is already Ubuntu installed
1549- checkIfUbuntuIsInstalled();
1550+ checkActivityStillValid();
1551 if (isFinishing()) return;
1552
1553- // verify if network is available for download.
1554- isNetworkAvailable();
1555-
1556 mInstallButton.setOnClickListener(mInstallButtonListener);
1557 IntentFilter filter = new IntentFilter();
1558 filter.addAction(UbuntuInstallService.AVAILABLE_CHANNELS);
1559 filter.addAction(UbuntuInstallService.DOWNLOAD_RESULT);
1560 filter.addAction(UbuntuInstallService.PROGRESS);
1561+ filter.addAction(UbuntuInstallService.PROGRESS_OUPUT);
1562 filter.addAction(UbuntuInstallService.INSTALL_RESULT);
1563- filter.addAction(UbuntuInstallService.VERSION_UPDATE);
1564- filter.addAction(UbuntuInstallService.SERVICE_STATE);
1565+ filter.addAction(UbuntuInstallService.SERVICE_STATE_CHANGED);
1566 registerReceiver(mServiceObserver, filter);
1567 mObserversRegistered = true;
1568
1569 // do we know last activity
1570- if (mStatus == InstallerState.DOWNLOADING || mStatus == InstallerState.INSTALLING) {
1571- // request last progress / status. this will update UI accordingly
1572- startService(new Intent(UbuntuInstallService.GET_PROGRESS_STATUS));
1573- } else {
1574- // READY + mDownloadedVersion != null => READY_TO_INSTALL
1575- mDownloadedVersion = UbuntuInstallService.getDownloadedVersion(this.getApplicationContext());
1576- if (mDownloadedVersion == null) {
1577- requestChannelList();
1578+ if (mInstallerService != null) {
1579+ InstallerState state = mInstallerService.getServiceState();
1580+ if (state == InstallerState.DOWNLOADING || state == InstallerState.INSTALLING) {
1581+ // request last progress / status. this will update UI accordingly
1582+ mProgressBar.setProgress(mInstallerService.getActionProgress());
1583 }
1584+ updateUiElements();
1585+ updateOuputTextview();
1586 }
1587- requestServiceState();
1588+ // register view tree observer for terminal
1589+ ViewTreeObserver vto = mTerminal.getViewTreeObserver();
1590+ vto.addOnGlobalLayoutListener(mTerminalLayoutObserver);
1591+
1592 }
1593
1594 @Override
1595@@ -131,6 +175,8 @@
1596 unregisterReceiver(mServiceObserver);
1597 }
1598 mObserversRegistered = false;
1599+ ViewTreeObserver vto = mTerminal.getViewTreeObserver();
1600+ vto.removeOnGlobalLayoutListener(mTerminalLayoutObserver);
1601 }
1602
1603 @Override
1604@@ -143,16 +189,12 @@
1605 @Override
1606 public boolean onOptionsItemSelected(MenuItem item) {
1607 switch (item.getItemId()) {
1608- case R.id.action_delete_download:
1609- // also attempt to uninstall ubuntu, since there should be none anyway, keep user data
1610- Intent action = new Intent(UbuntuInstallService.UNINSTALL_UBUNTU);
1611- action.putExtra(UbuntuInstallService.UNINSTALL_UBUNTU_EXTRA_REMOVE_USER_DATA, false);
1612+ case R.id.menu_action_delete_download:
1613+ Intent action = new Intent(UbuntuInstallService.DELETE_DOWNLOAD);
1614 startService(action);
1615- deleteDownload();
1616- mDownloadedVersion = null;
1617- requestServiceState();
1618+ Utils.showToast(this.getApplicationContext(), R.string.deleting_download);
1619 break;
1620- case R.id.action_dump_terminal:
1621+ case R.id.menu_action_dump_terminal:
1622 CharSequence terminalText = mTerminal.getText();
1623 if (terminalText.length() == 0) {
1624 Utils.showToast(this.getApplicationContext(), R.string.terminal_is_empty);
1625@@ -200,45 +242,42 @@
1626 context.startActivity(intent);
1627 }
1628
1629- private void checkIfUbuntuIsInstalled() {
1630+ /**
1631+ * Check is this activity is still valid, or we should switch to another one
1632+ */
1633+ protected void checkActivityStillValid() {
1634 // check is there is Ubuntu installed or is upgradeable
1635- if (UbuntuInstallService.isUbuntuInstalled(getApplicationContext())) {
1636- if(!UbuntuInstallService.isUpgradeable(getApplicationContext())) {
1637- Log.d(TAG, "go to launch screen, and kill Install activity");
1638- LaunchActivity.startFrom(this);
1639- finish();
1640- }
1641- }
1642+ if (mInstallerService != null
1643+ && mSp.getBoolean(UbuntuInstallService.PREF_KEY_UBUNTU_INSTALLED, false)
1644+ && mInstallerService.getServiceState() != InstallerState.INSTALLING
1645+ && mInstallerService.getServiceState() != InstallerState.DOWNLOADING ) {
1646+ Log.d(TAG, "go to launch screen, and kill Install activity");
1647+ LaunchActivity.startFrom(this);
1648+ mAvailableChannels = null;
1649+ finish();
1650+ }
1651 }
1652
1653 private void requestChannelList() {
1654- startService(new Intent(UbuntuInstallService.GET_CHANNEL_LIST));
1655- }
1656-
1657- private void requestServiceState() {
1658- startService(new Intent(UbuntuInstallService.GET_SERVICE_STATE));
1659- }
1660-
1661- private void deleteDownload() {
1662- Intent action = new Intent(UbuntuInstallService.CLEAN_DOWNLOAD);
1663- startService(action);
1664- }
1665-
1666+ mAvailableChannels = null;
1667+ Intent i = new Intent(UbuntuInstallService.GET_CHANNEL_LIST);
1668+ i.putExtra(UbuntuInstallService.GET_CHANNEL_LIST_EXTRA_SERVER_URL, mServerUrl);
1669+ startService(i);
1670+ }
1671+
1672 OnClickListener mInstallButtonListener = new OnClickListener() {
1673 @Override
1674 public void onClick(View v) {
1675 // do we need to download release, or there is already one downloaded
1676 // user might have missed SU request, then we have downloaded release and we just need deploy it
1677- // TODO: we will need to handle also download resume
1678- if (mStatus == InstallerState.DOWNLOADING) {
1679- Intent startInstall = new Intent(UbuntuInstallService.CANCEL_DOWNLOAD);
1680- startService(startInstall);
1681- } else if (mStatus == InstallerState.INSTALLING) {
1682- Intent startInstall = new Intent(UbuntuInstallService.CANCEL_INSTALL);
1683- startService(startInstall);
1684- } else if (UbuntuInstallService.checkifReadyToInstall(v.getContext())) {
1685- startInstallationIfPossible();
1686- } else if (0 != mAvailableChannels.size()) {
1687+ // we need to handle also download resume
1688+ if (mInstallerService.getServiceState() == InstallerState.DOWNLOADING) {
1689+ mInstallerService.pauseDownload();
1690+ } else if (UbuntuInstallService.isReadyToInstall(mSp)) {
1691+ UbuntuInstallService.startInstallationIfPossible(v.getContext());
1692+ } else if (UbuntuInstallService.isDownloadActive(mSp)) {
1693+ startService(new Intent(UbuntuInstallService.RESUME_DOWNLOAD));
1694+ } else if (mAvailableChannels != null && 0 != mAvailableChannels.size()) {
1695 // get list of aliases as array
1696 String channels[] = mAvailableChannels.keySet().toArray(new String[mAvailableChannels.size()]);
1697 // look for "trusty" as default channel
1698@@ -257,32 +296,21 @@
1699 true /* default latest settings*/).show();
1700 } else {
1701 // there are no channels to pick from, this was mistake, disable button
1702- requestServiceState();
1703+ mAvailableChannels = null;
1704+ updateUiElements();
1705 }
1706 }
1707 };
1708
1709- private void updateInfoOnUiThread(final String text) {
1710+ private void updateOuputTextview() {
1711 this.runOnUiThread(new Runnable() {
1712 @Override
1713 public void run() {
1714- // keep old text in there
1715- mTerminal.setText(mTerminal.getText().toString() + "\n" + text);
1716- final int scrollAmount = mTerminal.getLayout().getLineTop(mTerminal.getLineCount()) - mTerminal.getHeight();
1717- mTerminal.scrollTo(0, scrollAmount);
1718+ mTerminal.setText(mInstallerService.getActionOutput());
1719 }
1720 });
1721 }
1722-
1723- private void startInstallationIfPossible() {
1724- // TODO: check if we have battery and progress to install
1725- Intent startInstall = new Intent(UbuntuInstallService.INSTALL_UBUNTU);
1726- startService(startInstall);
1727- Utils.showToast(this, "Starting Ubuntu installation");
1728- // reset progress bar
1729- mProgressBar.setProgress(0);
1730- }
1731-
1732+
1733 TextPickerDialog.OnChannelPicktListener mInstallDialogListener
1734 = new TextPickerDialog.OnChannelPicktListener() {
1735 public void onChannelPicked(Context context, String channel, boolean bootstrap, boolean latest) {
1736@@ -297,16 +325,16 @@
1737
1738 private void startDownload(final String channel, final boolean bootstrap, final int version) {
1739 Intent startDownload = new Intent(UbuntuInstallService.DOWNLOAD_RELEASE);
1740- startDownload.putExtra(UbuntuInstallService.DOWNLOAD_RELEASE_EXTRA_CHANNEL_ALIAS, channel);
1741+ startDownload.putExtra(UbuntuInstallService.DOWNLOAD_RELEASE_EXTRA_SERVER_URL,
1742+ mServerUrl);
1743 startDownload.putExtra(UbuntuInstallService.DOWNLOAD_RELEASE_EXTRA_CHANNEL_URL,
1744- mAvailableChannels.get(channel));
1745+ mAvailableChannels.get(channel));
1746 startDownload.putExtra(UbuntuInstallService.DOWNLOAD_RELEASE_EXTRA_BOOTSTRAP, bootstrap);
1747 if (version != -1) {
1748 startDownload.putExtra(UbuntuInstallService.DOWNLOAD_RELEASE_EXTRA_VERSION, version);
1749 }
1750 startDownload.putExtra(UbuntuInstallService.DOWNLOAD_RELEASE_EXTRA_TYPE, ReleaseType.FULL.getValue());
1751 startService(startDownload);
1752- mTerminal.setText(R.string.downloading_starting);
1753 mProgressBar.setProgress(0);
1754 }
1755
1756@@ -317,9 +345,11 @@
1757 new Thread(new Runnable() {
1758 @Override
1759 public void run() {
1760- String jsonStr = Utils.httpDownload(UbuntuInstallService.BASE_URL
1761- + mAvailableChannels.get(channel));
1762- // TODO: handle malformed JSON
1763+ String jsonStr = Utils.httpDownload( mServerUrl + mAvailableChannels.get(channel));
1764+ if (jsonStr == null) {
1765+ Utils.showToast(context, "Wrong url to channel list");
1766+ return;
1767+ }
1768 final List<Image> releases = JsonChannelParser.getAvailableReleases(jsonStr, ReleaseType.FULL);
1769
1770 runOnUiThread(new Runnable() {
1771@@ -351,21 +381,45 @@
1772 }).start();
1773 }
1774
1775- private void updateUiElements() {
1776+ @Override
1777+ protected void onInstallerServiceConnected() {
1778+ updateUiElements();
1779+ updateOuputTextview();
1780+ }
1781+
1782+ protected void updateUiElements() {
1783+ checkActivityStillValid();
1784+ if (isFinishing()) return;
1785 this.runOnUiThread(new Runnable() {
1786 @Override
1787- public void run() {
1788- Log.v(TAG,"updateUiElements(" + mStatus + ")");
1789- switch (mStatus) {
1790+ public void run() {
1791+ InstallerState state = mInstallerService.getServiceState();
1792+ Log.v(TAG,"updateUiElements(" + state + ")");
1793+ // check if should fetch list of channels
1794+ if ( state == InstallerState.READY
1795+ && state != InstallerState.FETCHING_CHANNELS
1796+ && mAvailableChannels == null
1797+ && isNetworkAvailable()
1798+ && ! UbuntuInstallService.isDownloadActive(mSp)
1799+ && ! UbuntuInstallService.isReadyToInstall(mSp)) {
1800+ requestChannelList();
1801+ }
1802+ switch (state) {
1803 case READY:
1804 {
1805- if (mDownloadedVersion != null) {
1806+ if (UbuntuInstallService.isDownloadActive(mSp)) {
1807+ mInstallButton.setText(R.string.install_button_label_resume_download);
1808+ mInstallButton.setEnabled(true);
1809+ mProgressBar.setEnabled(false);
1810+ mProgressBar.setProgress(0);
1811+ mProgressText.setText("");
1812+ } else if (UbuntuInstallService.isReadyToInstall(mSp)) {
1813 mInstallButton.setText(R.string.install_button_label_resume);
1814 mInstallButton.setEnabled(true);
1815 mProgressBar.setEnabled(false);
1816 mProgressBar.setProgress(0);
1817 mProgressText.setText("");
1818- } else if (mAvailableChannels.size() > 0) {
1819+ } else if (mAvailableChannels != null && mAvailableChannels.size() > 0) {
1820 mInstallButton.setText(R.string.install_button_label_install);
1821 mInstallButton.setEnabled(true);
1822 mProgressBar.setEnabled(false);
1823@@ -388,18 +442,24 @@
1824 mProgressText.setText("");
1825 break;
1826 case DOWNLOADING:
1827- mInstallButton.setText(R.string.install_button_label_cancel_download);
1828+ mInstallButton.setText(R.string.install_button_label_pause_download);
1829 mInstallButton.setEnabled(true);
1830 mProgressBar.setEnabled(true);
1831 mProgressText.setText(R.string.downloading_release);
1832 break;
1833 case INSTALLING:
1834- mInstallButton.setText(R.string.install_button_label_cancel_install);
1835- mInstallButton.setEnabled(true);
1836+ mInstallButton.setText(R.string.install_button_label_installing);
1837+ mInstallButton.setEnabled(false);
1838 mProgressBar.setEnabled(true);
1839+ mProgressBar.setProgress(mInstallerService.getActionProgress());
1840 mProgressText.setText(R.string.installing_release);
1841 break;
1842+ default:
1843+ // ignore rest of the states
1844+ break;
1845 }
1846+ // dismiss notification we don't need
1847+ mInstallerService.dismissUbuntuNotification();
1848 }
1849 });
1850 }
1851@@ -410,63 +470,40 @@
1852 @Override
1853 public void onReceive(Context context, Intent intent) {
1854 String action = intent.getAction();
1855- Log.v(TAG,"mServiceObserver.onReceive(" + action + ")");
1856 // List of available channels fetched
1857 if (action.equals(UbuntuInstallService.AVAILABLE_CHANNELS)) {
1858- // ignore channel list if we have already downloaded release
1859- if (!UbuntuInstallService.checkifReadyToInstall(context)) {
1860- mAvailableChannels = (HashMap<String, String>)
1861- intent.getSerializableExtra(UbuntuInstallService.AVAILABLE_CHANNELS_EXTRA_CHANNELS);
1862- mStatus = InstallerState.READY;
1863- updateUiElements();
1864- }
1865+ Log.v(TAG,"mServiceObserver: available channels received");
1866+ mAvailableChannels = (HashMap<String, String>)
1867+ intent.getSerializableExtra(UbuntuInstallService.AVAILABLE_CHANNELS_EXTRA_CHANNELS);
1868 // Handle progress
1869 } else if (action.equals(UbuntuInstallService.PROGRESS)) {
1870- String p = intent.getStringExtra(UbuntuInstallService.PROGRESS_EXTRA_TEXT);
1871- int progress = intent.getIntExtra(UbuntuInstallService.PROGRESS_EXTRA_INT, -1);
1872- if (progress != -1) {
1873- mProgressBar.setProgress(progress);
1874- Log.d(TAG, "Progress: " + progress);
1875- }
1876- if (p != null && !p.equals("")) {
1877- // update terminal with progress text
1878- updateInfoOnUiThread(p);
1879- }
1880-
1881+ mProgressBar.setProgress(mInstallerService.getActionProgress());
1882+ } else if (action.equals(UbuntuInstallService.PROGRESS_OUPUT)) {
1883+ updateOuputTextview();
1884 // Handle install result
1885 } else if(action.equals(UbuntuInstallService.INSTALL_RESULT)) {
1886+ Log.v(TAG,"mServiceObserver: installaction finished");
1887 int r = intent.getIntExtra(UbuntuInstallService.INSTALL_RESULT_EXTRA_INT, -1);
1888 if (r == 0) {
1889 Utils.showToast(context, "Install completed");
1890 mProgressBar.setProgress(100);
1891- deleteDownload();
1892- // go to launch screen, and kill this activity.
1893- LaunchActivity.startFrom(context);
1894- finish();
1895 } else {
1896 Utils.showToast(context, "Installation failed:");
1897 String reason = intent.getStringExtra(UbuntuInstallService.INSTALL_RESULT_EXTRA_STR);
1898 Utils.showToast(context, reason);
1899- updateInfoOnUiThread(reason);
1900- // if we still have download go back to resume installation
1901- // TODO: we should distinguish between resume/retry
1902- mDownloadedVersion = UbuntuInstallService.getDownloadedVersion(context);
1903- if (UbuntuInstallService.checkifReadyToInstall(context)) {
1904- mDownloadedVersion = UbuntuInstallService.getDownloadedVersion(context);
1905- } else {
1906- deleteDownload();
1907- mDownloadedVersion = null;
1908- requestChannelList();
1909- }
1910- updateUiElements();
1911+ updateOuputTextview();
1912 }
1913+ updateUiElements();
1914
1915 // Handle download result
1916 } else if(action.equals(UbuntuInstallService.DOWNLOAD_RESULT)) {
1917+ Log.v(TAG,"mServiceObserver: download finished");
1918 int r = intent.getIntExtra(UbuntuInstallService.DOWNLOAD_RESULT_EXTRA_INT, -1);
1919 if (r == 0) {
1920- Utils.showToast(context, "Download completed, ready to install Ubuntu");
1921- startInstallationIfPossible();
1922+ if (mInstallerService.getServiceState() == InstallerState.READY) {
1923+ UbuntuInstallService.startInstallationIfPossible(context);
1924+ mInstallerService.resetActionOutput();
1925+ }
1926 } else {
1927 String reason = intent.getStringExtra(UbuntuInstallService.INSTALL_RESULT_EXTRA_STR);
1928 // make sure it was not cancelled by user
1929@@ -474,27 +511,13 @@
1930 reason = "Download failed: " + reason;
1931 }
1932 Utils.showToast(context, reason);
1933-
1934- // check if network is available for download.
1935- // isNetworkAvailable();
1936-
1937- updateInfoOnUiThread(reason);
1938- requestChannelList();
1939- }
1940- } else if (action.equals(UbuntuInstallService.VERSION_UPDATE)) {
1941- checkIfUbuntuIsInstalled();
1942- if (!isFinishing()) {
1943- // check what button should be shown
1944- if (UbuntuInstallService.checkifReadyToInstall(context)) {
1945- mDownloadedVersion = UbuntuInstallService.getDownloadedVersion(context);
1946- }
1947+ updateOuputTextview();
1948 updateUiElements();
1949 }
1950- } else if (action.equals(UbuntuInstallService.SERVICE_STATE)) {
1951- checkIfUbuntuIsInstalled();
1952+ } else if (action.equals(UbuntuInstallService.SERVICE_STATE_CHANGED)) {
1953+ Log.v(TAG,"mServiceObserver: service state changed");
1954+ checkActivityStillValid();
1955 if (!isFinishing()) {
1956- mStatus = InstallerState.fromOrdian(intent.getIntExtra(UbuntuInstallService.SERVICE_STATE, 0));
1957- mDownloadedVersion = UbuntuInstallService.getDownloadedVersion(context);
1958 updateUiElements();
1959 }
1960 }
1961@@ -507,7 +530,7 @@
1962 *
1963 * @return true if network is available
1964 */
1965- private boolean isNetworkAvailable () {
1966+ private boolean isNetworkAvailable() {
1967 final ConnectivityManager conMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
1968 final NetworkInfo activeNetwork = conMgr.getActiveNetworkInfo();
1969 if (activeNetwork != null && activeNetwork.isConnected()) {
1970@@ -516,11 +539,12 @@
1971
1972 Utils.createConfirmationDialog(this,
1973 R.string.no_network_dialog_title,
1974+ 0,
1975 R.string.enable_network,
1976 R.string.cancel,
1977 new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK),
1978- -1, // toastText
1979- -1, // choiceItemsArray
1980+ 0, // toastText
1981+ 0, // choiceItemsArray
1982 null, // defaultChoiceValues
1983 null // choiceClickListener
1984 ).show();
1985
1986=== modified file 'src/com/canonical/ubuntu/installer/JsonChannelParser.java'
1987--- src/com/canonical/ubuntu/installer/JsonChannelParser.java 2013-12-16 02:11:14 +0000
1988+++ src/com/canonical/ubuntu/installer/JsonChannelParser.java 2014-06-09 11:59:50 +0000
1989@@ -1,3 +1,20 @@
1990+/*
1991+ * This file is part of Ubuntu dualboot installer for Android.
1992+ * Copyright 2013 Canonical Ltd.
1993+ *
1994+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
1995+ * it under the terms of the GNU General Public License as published by
1996+ * the Free Software Foundation.
1997+ *
1998+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
1999+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2000+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
2001+ * See the GNU General Public License for more details.
2002+ *
2003+ * You should have received a copy of the GNU General Public License
2004+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
2005+ */
2006+
2007 package com.canonical.ubuntu.installer;
2008
2009 import java.util.Collections;
2010@@ -5,7 +22,7 @@
2011 import java.util.LinkedList;
2012 import java.util.List;
2013
2014-import com.canonical.ubuntu.installer.VersionInfo.ReleaseType;
2015+import com.canonical.ubuntu.installer.UbuntuInstallService.ReleaseType;
2016 import com.google.gson.Gson;
2017 import com.google.gson.JsonArray;
2018 import com.google.gson.JsonObject;
2019@@ -26,6 +43,7 @@
2020 }
2021
2022 public class Image {
2023+ Integer base;
2024 String description;
2025 Integer version;
2026 String type;
2027@@ -41,21 +59,25 @@
2028 static public List<Image> getAvailableReleases(String jsonStr, ReleaseType filter) {
2029 LinkedList<Image> releases = new LinkedList<Image>();
2030
2031- JsonObject index = new JsonParser().parse(jsonStr).getAsJsonObject();
2032- JsonArray images = index.get("images").getAsJsonArray();
2033- int size = images.size();
2034-
2035- for(int j = 0; j < size; j++) {
2036- Image image = new Gson().fromJson(images.get(j), Image.class);
2037- ReleaseType type = ReleaseType.UNKNOWN;
2038- if (FULL_RELEASE.equals(image.type)) {
2039- type = ReleaseType.FULL;
2040- } else if (DELTA_RELEASE.equals(image.type)) {
2041- type = ReleaseType.DELTA;
2042- }
2043- if (filter == type) {
2044- releases.add(image);
2045- }
2046+ try {
2047+ JsonObject index = new JsonParser().parse(jsonStr).getAsJsonObject();
2048+ JsonArray images = index.get("images").getAsJsonArray();
2049+ int size = images.size();
2050+
2051+ for(int j = 0; j < size; j++) {
2052+ Image image = new Gson().fromJson(images.get(j), Image.class);
2053+ ReleaseType type = ReleaseType.UNKNOWN;
2054+ if (FULL_RELEASE.equals(image.type)) {
2055+ type = ReleaseType.FULL;
2056+ } else if (DELTA_RELEASE.equals(image.type)) {
2057+ type = ReleaseType.DELTA;
2058+ }
2059+ if (filter == type) {
2060+ releases.add(image);
2061+ }
2062+ }
2063+ } catch (com.google.gson.JsonSyntaxException e) {
2064+ // ignore, empty list will be returned
2065 }
2066 // sort list
2067 Collections.sort(releases, imageComparator());
2068
2069=== modified file 'src/com/canonical/ubuntu/installer/LaunchActivity.java'
2070--- src/com/canonical/ubuntu/installer/LaunchActivity.java 2014-01-09 08:54:46 +0000
2071+++ src/com/canonical/ubuntu/installer/LaunchActivity.java 2014-06-09 11:59:50 +0000
2072@@ -1,12 +1,30 @@
2073+/*
2074+ * This file is part of Ubuntu dualboot installer for Android.
2075+ * Copyright 2013 Canonical Ltd.
2076+ *
2077+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
2078+ * it under the terms of the GNU General Public License as published by
2079+ * the Free Software Foundation.
2080+ *
2081+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
2082+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2083+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
2084+ * See the GNU General Public License for more details.
2085+ *
2086+ * You should have received a copy of the GNU General Public License
2087+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
2088+ */
2089+
2090 package com.canonical.ubuntu.installer;
2091
2092-import android.app.Activity;
2093-import android.app.AlertDialog;
2094+import java.util.Locale;
2095+
2096 import android.content.BroadcastReceiver;
2097 import android.content.Context;
2098 import android.content.DialogInterface;
2099 import android.content.Intent;
2100 import android.content.IntentFilter;
2101+import android.content.SharedPreferences;
2102 import android.os.Bundle;
2103 import android.util.Log;
2104 import android.view.Menu;
2105@@ -15,80 +33,121 @@
2106 import android.view.View.OnClickListener;
2107 import android.widget.TextView;
2108
2109+import com.canonical.ubuntu.installer.UbuntuInstallService.InstallerState;
2110 import com.canonical.ubuntu.widget.UbuntuButton;
2111
2112-public class LaunchActivity extends Activity {
2113+public class LaunchActivity extends BaseActivity {
2114 private static final String TAG = "UbuntuLaunchActivity";
2115- private UbuntuButton mRebootButton;
2116+
2117+ private SharedPreferences mSharedPreferences;
2118+ private TextView mTextTitle;
2119 private TextView mTextChannel;
2120 private TextView mTextChannelLabel;
2121+ private TextView mTextRefChannel;
2122+ private TextView mTextRefChannelLabel;
2123 private TextView mTextVersion;
2124 private TextView mTextVersionLabel;
2125- private TextView mTextTitle;
2126 private TextView mTextDescriptionLabel;
2127 private TextView mTextDescription;
2128- private VersionInfo mUbuntuVersion;
2129+ private TextView mTextUseRootfs;
2130+ private TextView mTextUseRootfsLabel;
2131+ private TextView mTextUseSystemData;
2132+ private TextView mTextUseSystemDataLabel;
2133+ private TextView mTextUseUserData;
2134+ private TextView mTextUseUserDataLabel;
2135+ private UbuntuButton mRebootButton;
2136+ private UbuntuButton mUpdateButton;
2137+ private boolean mAndroidUpdatePending;
2138+ private Menu mMenu;
2139
2140 @Override
2141 protected void onCreate(Bundle savedInstanceState) {
2142 super.onCreate(savedInstanceState);
2143- // check if Ubuntu is installed
2144- ensureUbuntuIsInstalled();
2145- if (isFinishing()) return;
2146-
2147+ doBindService();
2148+ mSharedPreferences = getSharedPreferences( UbuntuInstallService.SHARED_PREF, Context.MODE_PRIVATE);
2149 setContentView(R.layout.ubuntu_dualboot_launch);
2150- mRebootButton = (UbuntuButton) findViewById(R.id.download);
2151 mTextChannel = (TextView) findViewById(R.id.channel);
2152+ mTextChannelLabel = (TextView) findViewById(R.id.channel_label);
2153+ mTextRefChannel = (TextView) findViewById(R.id.ref_channel);
2154+ mTextRefChannelLabel = (TextView) findViewById(R.id.ref_channel_label);
2155 mTextVersion = (TextView) findViewById(R.id.version);
2156- mTextChannelLabel = (TextView) findViewById(R.id.channel_label);
2157 mTextVersionLabel = (TextView) findViewById(R.id.version_label);
2158- mTextTitle = (TextView) findViewById(R.id.title);
2159 mTextDescriptionLabel = (TextView) findViewById(R.id.description_label);
2160 mTextDescription = (TextView) findViewById(R.id.description);
2161+ mTextUseRootfs = (TextView) findViewById(R.id.rootfssize);
2162+ mTextUseRootfsLabel = (TextView) findViewById(R.id.rootfs_label);
2163+ mTextUseSystemData = (TextView) findViewById(R.id.system_data_size);
2164+ mTextUseSystemDataLabel = (TextView) findViewById(R.id.system_data_label);
2165+ mTextUseUserData = (TextView) findViewById(R.id.user_datasize);
2166+ mTextUseUserDataLabel = (TextView) findViewById(R.id.user_data_label);
2167+ mTextTitle = (TextView) findViewById(R.id.title);
2168 mTextTitle.setText(R.string.launch_title);
2169+ mUpdateButton = (UbuntuButton) findViewById(R.id.upgrade);
2170+ mRebootButton = (UbuntuButton) findViewById(R.id.reboot);
2171 mRebootButton.setText(R.string.reboot_button_label);
2172- fillInstalledVersionInfo();
2173-
2174- // check the upgradeable image.
2175- startService(new Intent(UbuntuInstallService.IS_UBUNTU_UPGRADABLE));
2176- }
2177-
2178+ mUpdateButton.setText(R.string.button_install_ubuntu_update);
2179+ mTextVersionLabel.setText(R.string.label_version);
2180+ mTextChannelLabel.setText(R.string.label_channel);
2181+ mTextRefChannelLabel.setText(R.string.label_channel_target);
2182+ mTextDescriptionLabel.setText(R.string.label_description);
2183+ mTextUseRootfsLabel.setText(R.string.label_rootfs);
2184+ mTextUseSystemDataLabel.setText(R.string.label_system_data);
2185+ mTextUseUserDataLabel.setText(R.string.label_user_data);
2186+ // check for updates
2187+ startService(new Intent(UbuntuInstallService.CHECK_PENDING_UPDATES));
2188+ }
2189+
2190+ @Override
2191+ protected void onDestroy() {
2192+ super.onDestroy();
2193+ doUnbindService();
2194+ }
2195+
2196 @Override
2197 public void onResume() {
2198 super.onResume();
2199 // check is there is Ubuntu installed
2200- ensureUbuntuIsInstalled();
2201+ checkActivityStillValid();
2202 if (isFinishing()) return;
2203
2204 mRebootButton.setOnClickListener(mRebootButtonListener);
2205+ mUpdateButton.setOnClickListener(mRebootButtonListener);
2206 IntentFilter filter = new IntentFilter();
2207- filter.addAction(UbuntuInstallService.VERSION_UPDATE);
2208+ filter.addAction(UbuntuInstallService.SERVICE_STATE_CHANGED);
2209+ filter.addAction(UbuntuInstallService.STORAGE_USE_UPDATED);
2210+ filter.addAction(UbuntuInstallService.ANDROID_UPDATE_PENDING);
2211+ filter.addAction(UbuntuInstallService.UBUNTU_UPDATE_PENDING);
2212+ filter.addAction(UbuntuInstallService.UBUNTU_UPDATE_AVAILABLE);
2213 registerReceiver(mServiceObserver, filter);
2214+ updateUiElements();
2215 }
2216
2217 @Override
2218 public void onPause() {
2219 super.onPause();
2220 mRebootButton.setOnClickListener(null);
2221+ mUpdateButton.setOnClickListener(null);
2222 // cancel observer if there is any
2223 unregisterReceiver(mServiceObserver);
2224 }
2225
2226 @Override
2227 public boolean onCreateOptionsMenu(Menu menu) {
2228- // Inflate the menu; this adds items to the action bar if it is present.
2229 getMenuInflater().inflate(R.menu.launcher_menu, menu);
2230+ menu.findItem(R.id.menu_action_update_android).setShowAsAction( MenuItem.SHOW_AS_ACTION_NEVER);
2231+ mMenu = menu;
2232 return true;
2233 }
2234
2235 @Override
2236 public boolean onOptionsItemSelected(MenuItem item) {
2237 switch (item.getItemId()) {
2238- case R.id.action_uninstall:
2239+ case R.id.menu_action_uninstall:
2240 // show dialog
2241 final Intent uninstall = new Intent(UbuntuInstallService.UNINSTALL_UBUNTU);
2242 Utils.createConfirmationDialog(this,
2243 R.string.uninstall_dialog_title,
2244+ R.string.uninstall_dialog_description,
2245 R.string.action_uninstall_button,
2246 R.string.cancel,
2247 uninstall,
2248@@ -103,71 +162,176 @@
2249 }
2250 }).show();
2251 break;
2252- case R.id.action_del_user_data:
2253- Intent action = new Intent(UbuntuInstallService.DELETE_UBUNTU_USER_DATA);
2254+ case R.id.menu_action_del_user_data:
2255+ final Intent action = new Intent(UbuntuInstallService.DELETE_UBUNTU_USER_DATA);
2256 Utils.createConfirmationDialog(this,
2257 R.string.action_delete_user_data,
2258+ 0,
2259 R.string.action_delete_udata_button,
2260 R.string.cancel,
2261 action,
2262 R.string.deleting_user_data,
2263- -1,
2264+ 0, // choiceItemsArray
2265 null,
2266 null).show();
2267 break;
2268+ case R.id.menu_action_update_storage_use:
2269+ final Intent updateStorage = new Intent(UbuntuInstallService.UPDATE_STORAGE_USE);
2270+ startService(updateStorage);
2271+ break;
2272+ case R.id.menu_action_check_for_update:
2273+ final Intent checkForUpdate = new Intent(UbuntuInstallService.CHECK_IF_UPDATE_AVAILABLE);
2274+ startService(checkForUpdate);
2275+ break;
2276+ case R.id.menu_action_update_android:
2277+ showAndroidUpdateDialog();
2278+ break;
2279 }
2280 return super.onOptionsItemSelected(item);
2281 }
2282
2283+ public void showAndroidUpdateDialog() {
2284+ final Intent update = new Intent(UbuntuInstallService.PREPARE_ANDROID_UPDATE);
2285+ Utils.createAndroidUpdateConfirmationDialog(this,
2286+ R.string.android_update_dialog_title,
2287+ R.string.android_update_dialog_description,
2288+ R.string.android_update_dialog_possitive,
2289+ R.string.cancel,
2290+ update,
2291+ 0,
2292+ R.array.update_android_options,
2293+ new boolean[]{mSharedPreferences.getBoolean(UbuntuInstallService.PREF_KEY_ANDROID_UPDATE_DISMISSED, false)},
2294+ new DialogInterface.OnMultiChoiceClickListener() {
2295+ @Override
2296+ public void onClick(DialogInterface dialog, int which, boolean isChecked) {
2297+ // update data in the shared preferences
2298+ mSharedPreferences.edit()
2299+ .putBoolean(UbuntuInstallService.PREF_KEY_ANDROID_UPDATE_DISMISSED, isChecked)
2300+ .commit();
2301+ mAndroidUpdatePending = !isChecked;
2302+ }
2303+ }).show();
2304+ }
2305+
2306 public static void startFrom(Context context) {
2307 Intent intent = new Intent(context, LaunchActivity.class);
2308 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
2309 context.startActivity(intent);
2310 }
2311
2312- private void fillInstalledVersionInfo() {
2313- mTextChannel.setText(mUbuntuVersion.getChannelAlias());
2314- mTextVersion.setText(Integer.toString(mUbuntuVersion.getVersion()));
2315- mTextDescription.setText(mUbuntuVersion.getDescription());
2316- mTextChannelLabel.setText(R.string.label_channel);
2317- mTextVersionLabel.setText(R.string.label_version);
2318- mTextDescriptionLabel.setText(R.string.label_description);
2319- }
2320-
2321+ @Override
2322+ protected void onInstallerServiceConnected() {
2323+ updateUiElements();
2324+ }
2325+
2326+ protected void updateUiElements() {
2327+ checkActivityStillValid();
2328+ if (isFinishing()) return;
2329+ this.runOnUiThread(new Runnable() {
2330+ @Override
2331+ public void run() {
2332+ String defValue = getString(R.string.not_available);
2333+ mTextChannel.setText(mSharedPreferences.getString(UbuntuInstallService.PREF_KEY_VERSION_CHANNEL, defValue));
2334+ mTextRefChannel.setText(mSharedPreferences.getString(UbuntuInstallService.PREF_KEY_VERSION_CHANNEL_TARGET, defValue));
2335+ mTextVersion.setText(mSharedPreferences.getString(UbuntuInstallService.PREF_KEY_VERSION_NUMBER, defValue));
2336+ mTextDescription.setText(mSharedPreferences.getString(UbuntuInstallService.PREF_KEY_VERSION_DESCRIPTION, defValue));
2337+ mTextUseRootfs.setText(formatSize(mSharedPreferences.getInt(UbuntuInstallService.PREF_KEY_SIZE_ROOTFS, -1)));
2338+ mTextUseSystemData.setText(formatSize(mSharedPreferences.getInt(UbuntuInstallService.PREF_KEY_SIZE_SYSTEM_DATA, -1)));
2339+ mTextUseUserData.setText(formatSize(mSharedPreferences.getInt(UbuntuInstallService.PREF_KEY_SIZE_USER_DATA, -1)));
2340+ if (mSharedPreferences.getBoolean(UbuntuInstallService.PREF_KEY_PENDING_UPDATE, false)) {
2341+ mUpdateButton.setVisibility(View.VISIBLE);
2342+ } else {
2343+ mUpdateButton.setVisibility(View.GONE);
2344+ }
2345+ // dismiss notification we don't need
2346+ if (mInstallerService != null) {
2347+ mInstallerService.dismissUbuntuNotification();
2348+ mInstallerService.dismissUbuntuUpdateNotification();
2349+ mInstallerService.dismissAndroidNotification();
2350+ }
2351+ if (mAndroidUpdatePending) {
2352+ showAndroidUpdateDialog();
2353+ }
2354+ }
2355+ });
2356+ }
2357+
2358+ String formatSize(int size) {
2359+ if (size != -1) {
2360+ return String.format(Locale.US,"%d MB", size);
2361+ } else {
2362+ return getString(R.string.not_available);
2363+ }
2364+ }
2365+
2366 /**
2367- * Get installed version to check Ubuntu is installed
2368+ * Check is this activity is still valid, or we should switch to another one
2369 */
2370- private void ensureUbuntuIsInstalled() {
2371- VersionInfo v = UbuntuInstallService.getInstalledVersion(this.getApplicationContext());
2372- if (v == null) {
2373+ private void checkActivityStillValid() {
2374+
2375+ if (! mSharedPreferences.getBoolean(UbuntuInstallService.PREF_KEY_UBUNTU_INSTALLED, false)
2376+ || (mInstallerService != null
2377+ && (mInstallerService.getServiceState() == InstallerState.INSTALLING
2378+ || mInstallerService.getServiceState() == InstallerState.DOWNLOADING)
2379+ )
2380+ ) {
2381 // go back to install screen, and release myself.
2382 InstallActivity.startFrom(this);
2383 finish();
2384- } else {
2385- mUbuntuVersion = v;
2386 }
2387 }
2388
2389 OnClickListener mRebootButtonListener = new OnClickListener() {
2390 @Override
2391 public void onClick(View v) {
2392- startService(new Intent(UbuntuInstallService.REBOOT_UBUNTU));
2393+ if (v.getId() == R.id.reboot) {
2394+ startService(new Intent(UbuntuInstallService.REBOOT_UBUNTU));
2395+ } else if (v.getId() == R.id.upgrade) {
2396+ if (UbuntuInstallService.isReadyToInstall(mSharedPreferences)) {
2397+ UbuntuInstallService.startInstallationIfPossible(v.getContext());
2398+ } else if (UbuntuInstallService.isDownloadActive(mSharedPreferences)) {
2399+ startService(new Intent(UbuntuInstallService.RESUME_DOWNLOAD));
2400+ } else {
2401+ startService(new Intent(UbuntuInstallService.DOWNLOAD_UPDATE));
2402+ }
2403+ }
2404 }
2405 };
2406
2407 BroadcastReceiver mServiceObserver = new BroadcastReceiver() {
2408- @SuppressWarnings("unchecked")
2409 @Override
2410- public void onReceive(Context context, Intent intent) {
2411- String action = intent.getAction();
2412- if (action.equals(UbuntuInstallService.VERSION_UPDATE)) {
2413- ensureUbuntuIsInstalled();
2414- if(UbuntuInstallService.isUpgradeable(getApplicationContext())) {
2415- Log.d(TAG, "Found upgradeable file, kill LaunchActivity");
2416- InstallActivity.startFrom(context);
2417- finish();
2418- }
2419+ public void onReceive(Context context, Intent intent) {
2420+ String action = intent.getAction();
2421+ if (action.equals(UbuntuInstallService.SERVICE_STATE_CHANGED)) {
2422+ InstallerState newState = mInstallerService.getServiceState();
2423+ Log.d(TAG,"New Service state:" + newState);
2424+ switch(newState) {
2425+ case DOWNLOADING:
2426+ case INSTALLING:
2427+ // switch back to install activity to minitor ongoing task
2428+ InstallActivity.startFrom(context);
2429+ finish();
2430+ break;
2431+ default:
2432+ checkActivityStillValid();
2433+ break;
2434+ }
2435+ } else if (action.equals(UbuntuInstallService.STORAGE_USE_UPDATED)) {
2436+ updateUiElements();
2437+ }else if (action.equals(UbuntuInstallService.ANDROID_UPDATE_PENDING)) {
2438+ if (!mSharedPreferences.getBoolean(UbuntuInstallService.PREF_KEY_ANDROID_UPDATE_DISMISSED, false)) {
2439+ mAndroidUpdatePending = true;
2440+ }
2441+ if (mMenu != null) {
2442+ mMenu.findItem(R.id.menu_action_update_android).setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS);
2443+ }
2444+ updateUiElements();
2445+ } else if (action.equals(UbuntuInstallService.UBUNTU_UPDATE_PENDING)) {
2446+ mUpdateButton.setVisibility(View.VISIBLE);
2447+ } else if (action.equals(UbuntuInstallService.UBUNTU_UPDATE_AVAILABLE)) {
2448+ mUpdateButton.setVisibility(View.VISIBLE);
2449 }
2450+
2451 }
2452 };
2453 }
2454
2455=== modified file 'src/com/canonical/ubuntu/installer/NumberPickerDialog.java'
2456--- src/com/canonical/ubuntu/installer/NumberPickerDialog.java 2013-12-13 01:06:14 +0000
2457+++ src/com/canonical/ubuntu/installer/NumberPickerDialog.java 2014-06-09 11:59:50 +0000
2458@@ -1,3 +1,20 @@
2459+/*
2460+ * This file is part of Ubuntu dualboot installer for Android.
2461+ * Copyright 2013 Canonical Ltd.
2462+ *
2463+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
2464+ * it under the terms of the GNU General Public License as published by
2465+ * the Free Software Foundation.
2466+ *
2467+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
2468+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2469+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
2470+ * See the GNU General Public License for more details.
2471+ *
2472+ * You should have received a copy of the GNU General Public License
2473+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
2474+ */
2475+
2476 package com.canonical.ubuntu.installer;
2477
2478 import com.canonical.ubuntu.installer.R;
2479@@ -5,15 +22,12 @@
2480 import android.app.AlertDialog;
2481 import android.content.Context;
2482 import android.content.DialogInterface;
2483-import android.content.SharedPreferences;
2484 import android.content.DialogInterface.OnClickListener;
2485 import android.content.res.Resources;
2486 import android.os.Bundle;
2487 import android.view.LayoutInflater;
2488 import android.view.View;
2489-import android.widget.CheckBox;
2490 import android.widget.NumberPicker;
2491-import android.widget.TextView;
2492
2493 /**
2494 * A text picker dialog that prompts the user for to select one of the text options
2495
2496=== modified file 'src/com/canonical/ubuntu/installer/TextPickerDialog.java'
2497--- src/com/canonical/ubuntu/installer/TextPickerDialog.java 2013-12-13 01:06:14 +0000
2498+++ src/com/canonical/ubuntu/installer/TextPickerDialog.java 2014-06-09 11:59:50 +0000
2499@@ -1,3 +1,20 @@
2500+/*
2501+ * This file is part of Ubuntu dualboot installer for Android.
2502+ * Copyright 2013 Canonical Ltd.
2503+ *
2504+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
2505+ * it under the terms of the GNU General Public License as published by
2506+ * the Free Software Foundation.
2507+ *
2508+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
2509+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2510+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
2511+ * See the GNU General Public License for more details.
2512+ *
2513+ * You should have received a copy of the GNU General Public License
2514+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
2515+ */
2516+
2517 package com.canonical.ubuntu.installer;
2518
2519 import com.canonical.ubuntu.installer.R;
2520@@ -13,7 +30,6 @@
2521 import android.view.View;
2522 import android.widget.CheckBox;
2523 import android.widget.NumberPicker;
2524-import android.widget.TextView;
2525
2526 /**
2527 * A text picker dialog that prompts the user for to select one of the text options
2528
2529=== modified file 'src/com/canonical/ubuntu/installer/UbuntuInstallService.java'
2530--- src/com/canonical/ubuntu/installer/UbuntuInstallService.java 2014-01-06 15:07:12 +0000
2531+++ src/com/canonical/ubuntu/installer/UbuntuInstallService.java 2014-06-09 11:59:50 +0000
2532@@ -1,7 +1,25 @@
2533+/*
2534+ * This file is part of Ubuntu dualboot installer for Android.
2535+ * Copyright 2013 Canonical Ltd.
2536+ *
2537+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
2538+ * it under the terms of the GNU General Public License as published by
2539+ * the Free Software Foundation.
2540+ *
2541+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
2542+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2543+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
2544+ * See the GNU General Public License for more details.
2545+ *
2546+ * You should have received a copy of the GNU General Public License
2547+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
2548+ */
2549+
2550 package com.canonical.ubuntu.installer;
2551
2552 import java.io.DataOutputStream;
2553 import java.io.File;
2554+import java.io.FileFilter;
2555 import java.io.FileNotFoundException;
2556 import java.io.FileOutputStream;
2557 import java.io.IOException;
2558@@ -22,18 +40,49 @@
2559 import org.json.JSONTokener;
2560
2561 import com.canonical.ubuntu.installer.JsonChannelParser.Image;
2562-import com.canonical.ubuntu.installer.VersionInfo.ReleaseType;
2563
2564+import android.annotation.SuppressLint;
2565 import android.app.IntentService;
2566+import android.app.Notification;
2567+import android.app.NotificationManager;
2568+import android.app.PendingIntent;
2569 import android.content.Context;
2570 import android.content.Intent;
2571+import android.content.IntentFilter;
2572 import android.content.SharedPreferences;
2573+import android.net.ConnectivityManager;
2574+import android.net.NetworkInfo;
2575+import android.net.wifi.WifiManager;
2576+import android.os.BatteryManager;
2577+import android.os.Binder;
2578+import android.os.Build;
2579+import android.os.IBinder;
2580 import android.os.PowerManager;
2581 import android.util.Log;
2582 import android.webkit.URLUtil;
2583
2584 public class UbuntuInstallService extends IntentService {
2585 private static final String TAG = "UbuntuInstallService";
2586+
2587+ // =================================================================================================
2588+ // Behaviour constants
2589+ // =================================================================================================
2590+ // Default update server address
2591+ public static final String SERVER_URL = "http://system-image.ubuntu.com";
2592+ // Flag if channel list should also include hidden channels
2593+ private static final boolean INCLUDE_HIDDEN = false;
2594+ // Flag if channel list should include saucy channels
2595+ private static final boolean INCLUDE_SAUCY = false;
2596+ // How often service should check for update for normal channel
2597+ public static final int UPDATE_CHECK_ALARM_HOURS = 48; // every 2 days
2598+ // How often service should check for update for proposed channel
2599+ public static final int UPDATE_CHECK_PROPOSED_ALARM_HOURS = 10; // every 10 hours
2600+
2601+ // Minimum battery charge in % for install to progress
2602+ public static final int MIN_INSTALL_BATTERY_CHARGE = 30;
2603+
2604+ // Bring up mode, support any device
2605+ public static final boolean BRINGUP_MODE = false;
2606
2607 // =================================================================================================
2608 // Shared preferences
2609@@ -41,14 +90,41 @@
2610 public final static String SHARED_PREF = "UInstallerPref";
2611 // Key for string value: absolute path to update file
2612 public final static String PREF_KEY_UPDATE_COMMAND = "update_command";
2613- // Key for String set value: version information: alias, Json, version, description
2614- private final static String PREF_KEY_DOWNLOADED_VERSION = "d_version";
2615- // Key for String set value: version information: alias, Json, version, description
2616- private final static String PREF_KEY_INSTALLED_VERSION = "i_version";
2617+
2618+ // Key strings describing active download
2619+ public final static String PREF_KEY_DOWNLOAD_ACTIVE = "d_active";
2620+ public final static String PREF_KEY_DOWNLOAD_SERVER = "server_url";
2621+ public final static String PREF_KEY_DOWNLOAD_CHANNEL = "channel_json";
2622+ public final static String PREF_KEY_DOWNLOAD_BOOTSTRAP = "bootstrap";
2623+ public final static String PREF_KEY_DOWNLOAD_VERSION = "d_version";
2624+ public final static String PREF_KEY_DOWNLOAD_TYPE = "d_type";
2625+ public final static String PREF_KEY_DOWNLOAD_COMPLETED = "d_completed";
2626+
2627+ // Key strings describing installed version
2628+ public static final String PREF_KEY_VERSION_NUMBER = "version_number";
2629+ public static final String PREF_KEY_VERSION_CHANNEL = "version_channel";
2630+ public static final String PREF_KEY_VERSION_CHANNEL_TARGET = "version_channel_target";
2631+ public static final String PREF_KEY_VERSION_DESCRIPTION = "version_description";
2632+ public static final String PREF_KEY_VERSION_BASE = "version_base";
2633+
2634+ public static final String PREF_KEY_UBUNTU_INSTALLED = "u_installed";
2635+
2636+ // Key for pending update, type boolean
2637+ public static final String PREF_KEY_PENDING_UPDATE = "pending_update";
2638+
2639+ // Key for android update notification being dismissed, type boolean
2640+ public static final String PREF_KEY_ANDROID_UPDATE_DISMISSED = "android_u_dismissed";
2641+
2642 // Key for boolean value: true if developer option is enabled
2643 public final static String PREF_KEY_DEVELOPER = "developer";
2644- // Key for int value: estimated number of checkpoints for installation
2645- public final static String PREF_KEY_ESTIMATED_CHECKPOINTS = "est_checkpoints";
2646+
2647+ // Kyes describing occupied storage space, all values are stored as string
2648+ // key for int value: rootfs size in MB
2649+ public final static String PREF_KEY_SIZE_ROOTFS = "size_rootfs";
2650+ // key for int value: system data size in MB
2651+ public final static String PREF_KEY_SIZE_SYSTEM_DATA = "size_system_data";
2652+ // key for int value: user data size in MB
2653+ public final static String PREF_KEY_SIZE_USER_DATA = "size_user_data";
2654
2655 // =================================================================================================
2656 // Default values to be used
2657@@ -70,66 +146,69 @@
2658 public final static String MAKO_PARTITION_RECOVERY = "/dev/block/platform/msm_sdcc.1/by-name/recovery";
2659 public final static String MANTA_PARTITION_BOOT = "/dev/block/platform/dw_mmc.0/by-name/boot";
2660 public final static String MANTA_PARTITION_RECOVERY = "/dev/block/platform/dw_mmc.0/by-name/recovery";
2661+ public final static String DEFAULT_PARTITION_BOOT = "/dev/bootimg"; // this is used for bringup devices
2662+ public final static String DEFAULT_PARTITION_RECOVERY = "/dev/recovery"; // this is used for bringup devices
2663+
2664+
2665+ // =================================================================================================
2666+ // Ubuntu parts
2667+ // =================================================================================================
2668+ public static final String UBUNTU_ROOTFS = "/data/system.img";
2669+ public static final String UBUNTU_SYSTEM_DATA = "/data/system-data";
2670+ public static final String UBUNTU_USER_DATA = "/data/user-data";
2671
2672 // =================================================================================================
2673 // Service Actions
2674+ public static final String BASE_PACKAGE = "com.canonical.ubuntuinstaller.UbuntuInstallService.";
2675 // =================================================================================================
2676 // Get list of channels
2677- public static final String GET_CHANNEL_LIST = "com.canonical.ubuntuinstaller.UbuntuInstallService.GET_CHANNEL_LIST";
2678+ public static final String GET_CHANNEL_LIST = BASE_PACKAGE + "GET_CHANNEL_LIST";
2679+ public static final String GET_CHANNEL_LIST_EXTRA_SERVER_URL = "url"; // string
2680 // Download latest release from given channel
2681- public static final String DOWNLOAD_RELEASE = "com.canonical.ubuntuinstaller.UbuntuInstallService.DOWNLOAD_RELEASE";
2682- public static final String DOWNLOAD_RELEASE_EXTRA_CHANNEL_ALIAS = "alias"; // string
2683- public static final String DOWNLOAD_RELEASE_EXTRA_CHANNEL_URL = "url"; // string
2684- public static final String DOWNLOAD_RELEASE_EXTRA_BOOTSTRAP = "bootstrap"; // boolean
2685- public static final String DOWNLOAD_RELEASE_EXTRA_VERSION = "version"; // int
2686- public static final String DOWNLOAD_RELEASE_EXTRA_TYPE = "type"; // JsonChannelParser.ReleaseType
2687- public static final String CANCEL_DOWNLOAD = "com.canonical.ubuntuinstaller.UbuntuInstallService.CANCEL_DOWNLOAD";
2688- public static final String PAUSE_DOWNLOAD = "com.canonical.ubuntuinstaller.UbuntuInstallService.PAUSE_DOWNLOAD";
2689- public static final String RESUME_DOWNLOAD = "com.canonical.ubuntuinstaller.UbuntuInstallService.RESUME_DOWNLOAD";
2690- public static final String CLEAN_DOWNLOAD = "com.canonical.ubuntuinstaller.UbuntuInstallService.CLEAN_DOWNLOADED";
2691- public static final String INSTALL_UBUNTU = "com.canonical.ubuntuinstaller.UbuntuInstallService.INSTALL_UBUNTU";
2692- public static final String CANCEL_INSTALL = "com.canonical.ubuntuinstaller.UbuntuInstallService.CANCEL_INSTALL";
2693- public static final String UNINSTALL_UBUNTU = "com.canonical.ubuntuinstaller.UbuntuInstallService.UINSTALL_UBUNTU";
2694- public static final String IS_UBUNTU_UPGRADABLE = "com.canonical.ubuntuinstaller.UbuntuInstallService.IS_UBUNTU_UPGRADABLE";
2695- public static final String UNINSTALL_UBUNTU_EXTRA_REMOVE_USER_DATA = "user_data";
2696- public static final String CHECK_FOR_UPDATE = "com.canonical.ubuntuinstaller.UbuntuInstallService.CHECK_FOR_UPDATE";
2697- public static final String DELETE_UBUNTU_USER_DATA = "com.canonical.ubuntuinstaller.UbuntuInstallService.DELETE_USER_DATA";
2698- public static final String GET_SERVICE_STATE = "com.canonical.ubuntuinstaller.UbuntuInstallService.GET_SERVICE_STATE";
2699- public static final String GET_PROGRESS_STATUS = "com.canonical.ubuntuinstaller.UbuntuInstallService.GET_PROGRESS_STATUS";
2700- public static final String REBOOT_UBUNTU = "com.canonical.ubuntuinstaller.UbuntuInstallService.REBOOT_UBUNTU";
2701+ public static final String DOWNLOAD_RELEASE = BASE_PACKAGE + "DOWNLOAD_RELEASE";
2702+ public static final String DOWNLOAD_RELEASE_EXTRA_SERVER_URL = "url_server"; // string
2703+ public static final String DOWNLOAD_RELEASE_EXTRA_CHANNEL_URL = "url_channel"; // string
2704+ public static final String DOWNLOAD_RELEASE_EXTRA_BOOTSTRAP = "bootstrap"; // boolean
2705+ public static final String DOWNLOAD_RELEASE_EXTRA_VERSION = "version"; // int
2706+ public static final String DOWNLOAD_RELEASE_EXTRA_TYPE = "type"; // JsonChannelParser.ReleaseType
2707+ public static final String DOWNLOAD_UPDATE = BASE_PACKAGE + "DOWNLOAD_UPDATE";
2708+ public static final String RESUME_DOWNLOAD = BASE_PACKAGE + "RESUME_DOWNLOAD";
2709+ public static final String DELETE_DOWNLOAD = BASE_PACKAGE + "DELETE_DOWNLOAD";
2710+ public static final String INSTALL_UBUNTU = BASE_PACKAGE + "INSTALL_UBUNTU";
2711+ public static final String UNINSTALL_UBUNTU = BASE_PACKAGE + "UINSTALL_UBUNTU";
2712+ public static final String UNINSTALL_UBUNTU_EXTRA_REMOVE_USER_DATA = "user_data";
2713+ public static final String DELETE_UBUNTU_USER_DATA = BASE_PACKAGE + "DELETE_UBUNTU_USER_DATA";
2714+ public static final String REBOOT_UBUNTU = BASE_PACKAGE + "REBOOT_UBUNTU";
2715+ public static final String UPDATE_STORAGE_USE = BASE_PACKAGE + "UPDATE_STORAGE_USE";
2716+ public static final String CHECK_IF_UPDATE_AVAILABLE = BASE_PACKAGE + "CHECK_IF_UPDATE_AVAILABLE";
2717+ public static final String CHECK_PENDING_UPDATES = BASE_PACKAGE + "CHECK_PENDING_UPDATES";
2718+ public static final String PREPARE_ANDROID_UPDATE = BASE_PACKAGE + "PREPARE_ANDROID_UPDATE";
2719
2720 // =================================================================================================
2721 // Service broadcast
2722 // =================================================================================================
2723- public static final String SERVICE_STATE = "com.canonical.ubuntuinstaller.UbuntuInstallService.SERVICE_STATE";
2724- public static final String SERVICE_STATE_EXTRA_STATE = "state"; // InstallerState enum
2725- public static final String AVAILABLE_CHANNELS = "com.canonical.ubuntuinstaller.UbuntuInstallService.AVAILABLE_CHANNELS";
2726- public static final String AVAILABLE_CHANNELS_EXTRA_CHANNELS = "channels"; // HashMap<String,String> channel aliases and json url
2727- public static final String DOWNLOAD_RESULT = "com.canonical.ubuntuinstaller.UbuntuInstallService.DOWNLOAD_RESULT";
2728- public static final String DOWNLOAD_RESULT_EXTRA_INT = "res_int"; // 0-success, -1 fail
2729- public static final String DOWNLOAD_RESULT_EXTRA_STR = "res_str"; // empty for success, or error text
2730- public static final String PROGRESS = "com.canonical.ubuntuinstaller.UbuntuInstallService.PROGRESS";
2731- public static final String PROGRESS_EXTRA_TEXT = "text"; // value will carry name of file currently downloaded
2732- public static final String PROGRESS_EXTRA_INT = "progress"; // value between 0-100 of current progress
2733- public static final String INSTALL_RESULT = "com.canonical.ubuntuinstaller.UbuntuInstallService.INSTALL_COMPLETED";
2734- public static final String INSTALL_RESULT_EXTRA_INT = "res_int"; // 0-success, -1 fail
2735- public static final String INSTALL_RESULT_EXTRA_STR = "res_str"; // empty for success, or error text
2736- public static final String VERSION_UPDATE = "com.canonical.ubuntuinstaller.UbuntuInstallService.VERSION_UPDATE";
2737- public static final String VERSION_UPDATE_EXTRA_VERSION = "version"; // int new version
2738- public static final String VERSION_UPDATE_EXTRA_DESCRIPTION = "description"; // string
2739- public static final String VERSION_UPDATE_EXTRA_ALIAS = "alias"; // string
2740+ public static final String SERVICE_STATE_CHANGED = BASE_PACKAGE + "SERVICE_STATE";
2741+ public static final String AVAILABLE_CHANNELS = BASE_PACKAGE + "AVAILABLE_CHANNELS";
2742+ public static final String AVAILABLE_CHANNELS_EXTRA_CHANNELS = "channels"; // HashMap<String,String> channel aliases and json url
2743+ public static final String DOWNLOAD_RESULT = BASE_PACKAGE + "DOWNLOAD_RESULT";
2744+ public static final String DOWNLOAD_RESULT_EXTRA_INT = "res_int"; // 0-success, -1 fail
2745+ public static final String DOWNLOAD_RESULT_EXTRA_STR = "res_str"; // empty for success, or error text
2746+ public static final String PROGRESS = BASE_PACKAGE + "PROGRESS";
2747+ public static final String PROGRESS_OUPUT = BASE_PACKAGE + "PROGRESS_OUTPUT";
2748+ public static final String INSTALL_RESULT = BASE_PACKAGE + "INSTALL_COMPLETED";
2749+ public static final String INSTALL_RESULT_EXTRA_INT = "res_int"; // 0-success, -1 fail
2750+ public static final String INSTALL_RESULT_EXTRA_STR = "res_str"; // empty for success, or error text
2751+ public static final String STORAGE_USE_UPDATED = BASE_PACKAGE + "STORAGE_USE_UPDATED";
2752+ public static final String ANDROID_UPDATE_PENDING = BASE_PACKAGE + "ANDROID_UPDATE";
2753+ public static final String UBUNTU_UPDATE_PENDING = BASE_PACKAGE + "UBUNTU_UPDATE_PENDING";
2754+ public static final String UBUNTU_UPDATE_AVAILABLE = BASE_PACKAGE + "UBUNTU_UPDATE_AVAILABLE";
2755
2756 // =================================================================================================
2757- // Download url strings
2758+ // Other constants
2759 // =================================================================================================
2760- public static final String BASE_URL = "http://system-image.ubuntu.com";
2761 private static final String CHANNELS_JSON = "/channels.json";
2762- private static final String URL_IMAGE_MASTER = "gpg/image-master.tar.xz";
2763- private static final String URL_IMAGE_SIGNING = "gpg/image-signing.tar.xz";
2764- private static final String ASC_SUFFIX = ".asc";
2765-
2766- // 2G for file system, 512M for swap.
2767- private static long INSTALL_SIZE_REQUIRED = (2048L + 512L) * 1024L * 1024L;
2768+ // 2G for file system
2769+ private static long INSTALL_SIZE_REQUIRED = 2048L * 1024L * 1024L;
2770 // 15M extra space to keep it safe.
2771 private static long EXTRA_SIZE_REQUIRED = 15 * 1024 * 1024;
2772
2773@@ -138,7 +217,7 @@
2774 * State of the service
2775 */
2776 public enum InstallerState {
2777- READY, FETCHING_CHANNELS, DOWNLOADING, INSTALLING, UNINSTALLING, DELETING_USER_DATA;
2778+ READY, FETCHING_CHANNELS, DOWNLOADING, INSTALLING, UNINSTALLING, DELETING_USER_DATA, CALCULATING_USAGE;
2779 public static InstallerState fromOrdian(int ordianl) {
2780 return InstallerState.values()[ordianl];
2781 }
2782@@ -152,53 +231,76 @@
2783 private static final String TAR = "u_tar";
2784 private static final String ANDROID_LOOP_MOUNT = "aloopmount";
2785 private static final String ANDROID_BOOTMGR = "bootmgr";
2786- private static final String UPGRADECHECKER = "upgrade-checker";
2787 private static final String UPDATE_SCRIPT = "system-image-upgrader";
2788- private static final String ARCHIVE_MASTER = "archive-master.tar.xz";
2789- private static final String ARCHIVE_MASTER_ASC = "archive-master.tar.xz.asc";
2790- private static final String U_REBOOT_APP = "u-reboot-app.tar.xz";
2791- private static final String U_REBOOT_APP_ASC = "u-reboot-app.tar.xz.asc";
2792+ public static final String U_REBOOT_APP = "u-reboot-app.tar.xz";
2793+ public static final String RECOVERY_REMOUNT = "recovery-remount.tar.xz";
2794
2795 // =================================================================================================
2796 // Update command file constants
2797- // =================================================================================================
2798- private static final String UPDATE_COMMAND = "update_command";
2799- private static final String COMMAND_FORMAT = "format";
2800- private static final String COMMAND_MOUNT = "mount";
2801- private static final String COMMAND_UMOUNT = "unmount";
2802- private static final String COMMAND_LOAD_KEYRING = "load_keyring";
2803- private static final String COMMAND_UPDATE = "update";
2804- private static final String PARTITION_DATA = "data";
2805- private static final String PARTITION_SYSTEM = "system";
2806+ // =================================================================================================
2807+ public static final String UPDATE_COMMAND = "update_command";
2808+ public static final String COMMAND_FORMAT = "format";
2809+ public static final String COMMAND_MOUNT = "mount";
2810+ public static final String COMMAND_UMOUNT = "unmount";
2811+ public static final String COMMAND_UPDATE = "update";
2812+ public static final String PARTITION_DATA = "data";
2813+ public static final String PARTITION_SYSTEM = "system";
2814
2815 // other constants
2816+ public static final String CUSTOM_SERVER="custom_server";
2817 private static final String RELEASE_FOLDER = "/ubuntu_release";
2818+ private static final String LINKED_UPGRADER_COMMAND = "recovery/ubuntu_command";
2819+ private static final String PENDING_ANDROID_UPDATE = "android_update";
2820 private static final String TEMP_FOLDER = "/uTemp";
2821 private static final int PROGRESS_UBUNTU_ADJUSTMENT = 563979;
2822 private static final int PROGRESS_DEVICE_ADJUSTMENT = 651093;
2823 private static final int PROGRESS_CUSTOM_ADJUSTMENT = 2036039;
2824- private static final int PROGRESS_SWAP_CREATION_ADJUSTMENT = 85; // equivalent of time tar --checkpoint=200
2825- private static final int PROGRESS_MKSWAP_ADJUSTMENT = 17; // equivalent of time tar --checkpoint=200
2826- private PowerManager mPowerManager;
2827 private PowerManager.WakeLock mWakeLock;
2828- // FIXME make workPath in Cache a private function
2829- private boolean workPathInCache = false;
2830- private String mRootOfWorkPath;
2831+ private WifiManager.WifiLock mWifiLock;
2832 private volatile boolean mIsCanceled;
2833+ private boolean mBringup;
2834
2835 // progress values
2836 private long mProgress; // so far handled amount downloaded/processed
2837 private int mLastSignalledProgress;
2838+ private String mActionOutput;
2839 private long mTotalSize; // calculated
2840- private InstallerState mInstallerState;
2841+ private InstallerState mServiceState;
2842+ private File mReleaseFolder;
2843+ private SharedPreferences mSharedPreferences;
2844+ private Notification mUbuntuNotification;
2845+ private Notification mUbuntuUpdateNotification;
2846+ private Notification mUbuntuProgressNotification;
2847+ private Notification mAndroidUpdateNotification;
2848+ NotificationManager mNotificationManager;
2849
2850- public class Channel {
2851- String alias;
2852- File[] files;
2853- boolean hiden;
2854+ public enum ReleaseType {
2855+ FULL(0),
2856+ DELTA(1),
2857+ UNKNOWN(2);
2858+ private final int value;
2859+
2860+ private ReleaseType(final int newValue) {
2861+ value = newValue;
2862+ }
2863+
2864+ public int getValue() {
2865+ return value;
2866+ }
2867+
2868+ public static ReleaseType fromValue(final int value) {
2869+ switch (value) {
2870+ case 0: // FULL
2871+ return FULL;
2872+ case 1: // DELTA
2873+ return DELTA;
2874+ }
2875+ return UNKNOWN;
2876+ }
2877 };
2878
2879 class ECancelException extends Exception {
2880+ private static final long serialVersionUID = 1L;
2881 long mDownloadedSize;
2882
2883 public ECancelException(long downloadedSize){
2884@@ -208,6 +310,8 @@
2885 };
2886
2887 class ESumNotMatchException extends Exception {
2888+ private static final long serialVersionUID = 1L;
2889+
2890 public ESumNotMatchException(){
2891 super();
2892 }
2893@@ -218,6 +322,7 @@
2894 };
2895
2896 class EShellExecException extends Exception {
2897+ private static final long serialVersionUID = 1L;
2898 public EShellExecException(){
2899 super();
2900 }
2901@@ -228,134 +333,195 @@
2902 };
2903
2904 public UbuntuInstallService() {
2905- super(TAG);
2906- }
2907+ super(UbuntuInstallService.class.getName());
2908+ }
2909+
2910+ /**
2911+ * Class for clients to get hold of this service
2912+ * this only works as long as service runs in same process as client
2913+ */
2914+ public class LocalBinder extends Binder {
2915+ UbuntuInstallService getService() {
2916+ return UbuntuInstallService.this;
2917+ }
2918+ }
2919+
2920+ @Override
2921+ public IBinder onBind(Intent intent) {
2922+ return mBinder;
2923+ }
2924+ private final IBinder mBinder = new LocalBinder();
2925
2926 @Override
2927 public void onCreate() {
2928 super.onCreate();
2929- mPowerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
2930- // SharedPreferences sharedPref = this.getSharedPreferences(, MODE_PRIVATE);
2931- // do we have cache permissions?
2932- File testDir = new File("/cache/testDir");
2933- if (testDir.mkdir()) {
2934- testDir.delete();
2935- mRootOfWorkPath = "/cache";
2936- workPathInCache = true;
2937- } else {
2938- mRootOfWorkPath = getFilesDir().toString(); // "/data/data/com.canonical.ubuntu.installer/files";
2939- workPathInCache = false;
2940- }
2941- mInstallerState = InstallerState.READY;
2942- }
2943-
2944- @Override
2945- public int onStartCommand(Intent intent, int flags, int startId) {
2946- // if service is not in ready state, handle specific requests here
2947- if (mInstallerState != InstallerState.READY) {
2948- String action = intent.getAction();
2949- if (action.equals(CANCEL_DOWNLOAD)) {
2950- // set the cancel flag, but let it remove downloaded files on worker thread
2951- mIsCanceled = true;
2952- } else if (action.equals(GET_PROGRESS_STATUS)) {
2953- broadcastProgress(mLastSignalledProgress, "");
2954- } else if (action.equals(GET_SERVICE_STATE)) {
2955- broadcastInstallerState();
2956- }
2957- }
2958- return super.onStartCommand(intent, flags, startId);
2959- }
2960-
2961+ mReleaseFolder = new File(getFilesDir(), RELEASE_FOLDER);
2962+ mServiceState = InstallerState.READY;
2963+ mSharedPreferences = getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
2964+ mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
2965+ mWakeLock = ((PowerManager)getSystemService(Context.POWER_SERVICE))
2966+ .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ubuntu-dualboot");
2967+ WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
2968+ mWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG);
2969+ mBringup = Utils.isBringupMode();
2970+ }
2971+
2972+ /**
2973+ * Get service state
2974+ * @return service state
2975+ */
2976+ public InstallerState getServiceState(){
2977+ return mServiceState;
2978+ }
2979+
2980+ /**
2981+ * Get ongoing action progress
2982+ * @return last known progress
2983+ */
2984+ public int getActionProgress() {
2985+ return mLastSignalledProgress;
2986+ }
2987+
2988+ /**
2989+ * Pause ongoing download
2990+ */
2991+ public void pauseDownload() {
2992+ if (mServiceState == InstallerState.DOWNLOADING) {
2993+ mIsCanceled = true;
2994+ }
2995+ }
2996+
2997+ /**
2998+ * Get action output
2999+ * @return action output buffer
3000+ */
3001+ public String getActionOutput() {
3002+ return mActionOutput;
3003+ }
3004+
3005+ /**
3006+ * Reset action output buffer
3007+ */
3008+ public void resetActionOutput() {
3009+ mActionOutput = "";
3010+ }
3011+
3012 @Override
3013 protected void onHandleIntent(Intent intent) {
3014+ // reset progress for new action
3015+ if (intent == null) {
3016+ // ignore
3017+ return;
3018+ }
3019+ mLastSignalledProgress = 0;
3020 String action = intent.getAction();
3021+ String debugAction = action;
3022+ if (action.startsWith(BASE_PACKAGE)) {
3023+ debugAction = action.substring(BASE_PACKAGE.length());
3024+ }
3025+ Log.d(TAG, "onHandleIntent<<: " + debugAction);
3026 Intent result = null;
3027-
3028- Log.d(TAG, this.toString() + " onHandleIntent: " + action);
3029 if (action.equals(GET_CHANNEL_LIST)) {
3030 updateInstallerState(InstallerState.FETCHING_CHANNELS);
3031- result = doGetChannelList(intent);
3032+ result = getChannelList(intent);
3033 } else if (action.equals(DOWNLOAD_RELEASE)) {
3034 updateInstallerState(InstallerState.DOWNLOADING);
3035- result = doDownloadRelease(intent);
3036- } else if (action.equals(CANCEL_DOWNLOAD)) {
3037- // download should be already cancelled, don't delete files for might resume latter
3038- // result = doRemoreDownload(intent);
3039- result = new Intent(SERVICE_STATE);
3040- result.putExtra(SERVICE_STATE_EXTRA_STATE, mInstallerState.ordinal());
3041- } else if (action.equals(PAUSE_DOWNLOAD)) {
3042- // TODO: handle download
3043+ result = downloadRelease(intent);
3044+ } else if (action.equals(DOWNLOAD_UPDATE)) {
3045+ updateInstallerState(InstallerState.DOWNLOADING);
3046+ result = downloadUpdate();
3047 } else if (action.equals(RESUME_DOWNLOAD)) {
3048 updateInstallerState(InstallerState.DOWNLOADING);
3049- // TODO: handle download
3050- } else if (action.equals(CLEAN_DOWNLOAD)) {
3051- result = doRemoreDownload(intent);
3052+ result = resumeDownload();
3053+ } else if (action.equals(DELETE_DOWNLOAD)) {
3054+ doDeleteDownloadedRelease();
3055 } else if (action.equals(INSTALL_UBUNTU)) {
3056 updateInstallerState(InstallerState.INSTALLING);
3057- result = doInstallUbuntu(intent);
3058- } else if (action.equals(IS_UBUNTU_UPGRADABLE)) {
3059+ result = doInstallUbuntu();
3060+ } else if (action.equals(CHECK_PENDING_UPDATES)) {
3061 // check if the upgradeable images available.
3062- if(findInstallCommand()) {
3063- Log.d(TAG, "There is a upgradeable file. send VERSION_UPDATE");
3064- result = new Intent(VERSION_UPDATE);
3065- }
3066- } else if (action.equals(CANCEL_INSTALL)) {
3067- // install should be already cancelled, try to delete it now
3068- updateInstallerState(InstallerState.UNINSTALLING);
3069- result = doUninstallUbuntu(intent);
3070+ findPendingUpdates();
3071+ } else if (action.equals(CHECK_IF_UPDATE_AVAILABLE)) {
3072+ checkForAvailableUpdate();
3073 } else if (action.equals(UNINSTALL_UBUNTU)) {
3074 updateInstallerState(InstallerState.UNINSTALLING);
3075- result = doUninstallUbuntu(intent);
3076+ doUninstallUbuntu(intent);
3077 } else if (action.equals(DELETE_UBUNTU_USER_DATA)) {
3078 updateInstallerState(InstallerState.DELETING_USER_DATA);
3079- result = doDeleteUbuntuUserData(intent);
3080+ deleteUbuntuUserData();
3081 } else if(action.equals(REBOOT_UBUNTU)) {
3082- Log.d(TAG, this.toString() + ": REBOOT_UBUNTU");
3083- doReboot(intent);
3084- return;
3085+ rebootToUbuntu();
3086+ return;
3087+ } else if (action.equals(PREPARE_ANDROID_UPDATE)) {
3088+ prepareAndroidUpdate();
3089+ return;
3090+ } else if (action.equals(UPDATE_STORAGE_USE)) {
3091+ updateInstallerState(InstallerState.CALCULATING_USAGE);
3092+ doUpdateStorageUse();
3093 } else {
3094 // for any other request broadcast service state
3095- result = new Intent(SERVICE_STATE);
3096- result.putExtra(SERVICE_STATE_EXTRA_STATE, mInstallerState.ordinal());
3097+ result = new Intent(SERVICE_STATE_CHANGED);
3098 }
3099 if (result != null) {
3100 sendBroadcast(result);
3101 }
3102 updateInstallerState(InstallerState.READY);
3103- Log.d(TAG, this.toString() + " onHandleIntent: " + action + " END");
3104+ Log.d(TAG, " onHandleIntent>>: " + debugAction);
3105 }
3106
3107- private Intent doGetChannelList(Intent intent) {
3108+ private Intent getChannelList(Intent intent) {
3109+ String serverUrl = intent.getStringExtra(GET_CHANNEL_LIST_EXTRA_SERVER_URL);
3110+ if (serverUrl == null) {
3111+ // use default server
3112+ serverUrl = Utils.getReleaseServerUrl(this);;
3113+ }
3114+ boolean includeProposed = mSharedPreferences.getBoolean(PREF_KEY_DEVELOPER, false);
3115+ HashMap<String, String> channels = doGetChannels(serverUrl, true, INCLUDE_SAUCY, INCLUDE_HIDDEN, includeProposed );
3116 Intent result = new Intent(AVAILABLE_CHANNELS);
3117-
3118- HashMap<String, String> channels= new HashMap<String, String>();
3119- boolean includeHidden = getSharedPreferences( SHARED_PREF, Context.MODE_PRIVATE).getBoolean(PREF_KEY_DEVELOPER, false);
3120+ result.putExtra(AVAILABLE_CHANNELS_EXTRA_CHANNELS, channels);
3121+ return result;
3122+ }
3123+
3124+ private HashMap<String, String> doGetChannels( String serverAddress,
3125+ boolean friendlyName,
3126+ boolean includeSaucy,
3127+ boolean includeHiden,
3128+ boolean includeProposed) {
3129+ HashMap<String, String> channels = new HashMap<String, String>();
3130 String deviceModel = Utils.getDeviceModel();
3131- String channelJsonStr = Utils.httpDownload(BASE_URL + CHANNELS_JSON);
3132+ String channelJsonStr = Utils.httpDownload(serverAddress + CHANNELS_JSON);
3133 if (channelJsonStr != null) {
3134 JSONObject list;
3135 try {
3136 list = (JSONObject) new JSONTokener(channelJsonStr).nextValue();
3137+ @SuppressWarnings("unchecked")
3138 Iterator<String> keys = list.keys();
3139 while(keys.hasNext()){
3140 String key = keys.next();
3141- JSONObject channel = list.optJSONObject(key);
3142+ final JSONObject channel = list.optJSONObject(key);
3143 if (channel != null) {
3144- JSONObject devices = channel.optJSONObject("devices");
3145+ final JSONObject devices = channel.optJSONObject("devices");
3146 if (devices != null) {
3147- JSONObject device = devices.optJSONObject(deviceModel);
3148+ final JSONObject device = devices.optJSONObject(deviceModel);
3149 if (device != null) {
3150- String url = device.optString("index");
3151+ final String url = device.optString("index");
3152 if (url != null) {
3153 // bingo, add to list if not hidden or developer
3154- boolean hidden = channel.optBoolean("hiden"); // by default not hidden
3155- String alias = channel.optString("alias");
3156- if (alias == null || alias.equals("")) {
3157- alias = key; // use key instead
3158+ final boolean hidden = channel.optBoolean("hidden"); // by default not hidden
3159+ // try to remove "ubuntu-touch/" so it's more human friendly
3160+ if (friendlyName && key.startsWith("ubuntu-touch/")) {
3161+ key = key.substring("ubuntu-touch/".length());
3162 }
3163- // Log.v(TAG, "Channel:" + alias + " url:" + url);
3164- if (!hidden || includeHidden) {
3165- channels.put(alias, url);
3166+ // Log.v(TAG, "Channel:" + key + " url:" + url);
3167+ if (
3168+ ( includeSaucy || ! key.contains("saucy"))
3169+ && (
3170+ !hidden
3171+ || includeHiden
3172+ || ( includeProposed && key.contains("-proposed"))
3173+ )
3174+ ) {
3175+ channels.put(key, url);
3176 }
3177 }
3178 }
3179@@ -363,44 +529,29 @@
3180 }
3181 }
3182 } catch (JSONException e) {
3183- // TODO Auto-generated catch block
3184 e.printStackTrace();
3185 }
3186 }
3187- result.putExtra(AVAILABLE_CHANNELS_EXTRA_CHANNELS, channels);
3188- return result;
3189+ return channels;
3190 }
3191
3192- private Intent doRemoreDownload(Intent intent) {
3193- Intent result = new Intent(SERVICE_STATE);
3194- String s = deleteRelease();
3195- if (s!= null) {
3196- broadcastProgress(-1, s);
3197- // delete failed
3198- }
3199- result.putExtra(SERVICE_STATE, InstallerState.READY.ordinal());
3200- return result;
3201- }
3202-
3203- private Intent doInstallUbuntu(Intent intent) {
3204+ private Intent doInstallUbuntu() {
3205 Log.w(TAG, "doInstallUbuntu");
3206 Intent result = new Intent(INSTALL_RESULT);
3207
3208 // get update command file
3209 String updateCommand = getUpdateCommand();
3210-
3211- // 1. check if update command exist.
3212- // 2. However, if the udpate command is in "/cache",
3213- // the app can not access to /cache sometimes.
3214+ dismissUbuntuNotification();
3215+ // check if update command exist.
3216 if (updateCommand.equals("") ||
3217- (!new File(updateCommand).exists() &&
3218- !updateCommand.startsWith("/cache"))) {
3219- return handleInstallFail(result, -1, "Missing update command");
3220+ (!new File(updateCommand).exists())) {
3221+
3222+ return handleInstallError(result, -1, "Missing update command");
3223 }
3224
3225- SharedPreferences pref = getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
3226- mTotalSize = pref.getInt(PREF_KEY_ESTIMATED_CHECKPOINTS, 0);
3227+ mTotalSize = estimateCheckpooint(updateCommand);
3228 mLastSignalledProgress = 0;
3229+ mProgress = 0;
3230
3231 List<String> shellcmds = new ArrayList<String>();
3232 {
3233@@ -427,37 +578,68 @@
3234 UBUNTU_BOOT_IMG,
3235 Utils.getRecoveryPartitionPath()
3236 ));
3237+ // remove downloaded release
3238+ shellcmds.add(String.format("rm -rf %s\n", new File(updateCommand).getParent()));
3239 shellcmds.add("exit");
3240 }
3241
3242- broadcastProgress(-1, "Starting update script - " + updateCommand);
3243+ broadcastProgress("Starting update script - " + updateCommand);
3244 try {
3245 int ret = executeSUCommands(shellcmds.toArray(new String[shellcmds.size()]));
3246 result.putExtra(INSTALL_RESULT_EXTRA_INT, ret);
3247 } catch (EShellExecException e) {
3248- return handleInstallFail(result, -1, e.getMessage());
3249+ return handleInstallError(result, -1, e.getMessage());
3250 }
3251-
3252+ Log.d(TAG, "doInstallUbuntu-update script finished");
3253+ mLastSignalledProgress = 100;
3254+ handleInstallFinish();
3255+ return result;
3256+ }
3257+
3258+ /**
3259+ * Returns true if download or install is update operation
3260+ * @return
3261+ */
3262+ private boolean isUpdate() {
3263+ // if not defined, default value is DELTA, for updates downloaded by Ubuntu upgrader
3264+ return ( ReleaseType.DELTA == ReleaseType.fromValue(
3265+ mSharedPreferences.getInt(PREF_KEY_DOWNLOAD_TYPE, ReleaseType.DELTA.getValue())));
3266+ }
3267+
3268+ private void handleInstallFinish() {
3269 // we done.
3270+ dismissUbuntuUpdateNotification();
3271 cleanUpdateCommand();
3272- VersionInfo v = new VersionInfo(pref, PREF_KEY_DOWNLOADED_VERSION);
3273- v.storeVersion(pref.edit(), PREF_KEY_INSTALLED_VERSION);
3274- mProgress = 100;
3275- return result;
3276+ checkChannelAddress();
3277+ mSharedPreferences.edit()
3278+ .putBoolean(PREF_KEY_UBUNTU_INSTALLED, true)
3279+ .putBoolean(PREF_KEY_PENDING_UPDATE, false)
3280+ .commit();
3281+ Utils.registerUpdateCheckAlarm(this, mSharedPreferences);
3282+ // update notifications
3283+ if ( isUpdate()) {
3284+ showUbuntuNotification(R.string.notification_update_installed, LaunchActivity.class);
3285+ } else {
3286+ showUbuntuNotification(R.string.notification_ubuntu_installed, LaunchActivity.class);
3287+ }
3288 }
3289
3290- private Intent handleInstallFail(Intent i, int res, String failReason) {
3291+ private Intent handleInstallError(Intent i, int res, String reason) {
3292+ mActionOutput += "\n" + reason;
3293+ if (isUpdate()) {
3294+ showUbuntuNotification(R.string.notification_installation_failed, LaunchActivity.class);
3295+ } else {
3296+ showUbuntuNotification(R.string.notification_installation_failed, InstallActivity.class);
3297+ }
3298 i.putExtra(INSTALL_RESULT_EXTRA_INT, -1);
3299- i.putExtra(INSTALL_RESULT_EXTRA_STR, failReason);
3300- // we don't want to unstainll if we failed to install a update.
3301- // doUninstallUbuntu(i);
3302+ i.putExtra(INSTALL_RESULT_EXTRA_STR, reason);
3303 return i;
3304 }
3305
3306- private Intent doUninstallUbuntu(Intent intent) {
3307- File workingFolder = new File(mRootOfWorkPath, TEMP_FOLDER);
3308+ private void doUninstallUbuntu(Intent intent) {
3309+ mActionOutput = "";
3310+ File workingFolder = new File(getFilesDir(), TEMP_FOLDER);
3311 File updateCommand = new File(workingFolder, UPDATE_COMMAND);
3312- Intent result = new Intent(VERSION_UPDATE);
3313 boolean removeUserData = intent.getBooleanExtra(UNINSTALL_UBUNTU_EXTRA_REMOVE_USER_DATA, false);
3314 Log.d(TAG, "doUninstallUbuntu");
3315
3316@@ -476,7 +658,7 @@
3317
3318 // 1. force unmount
3319 // 2. restore android recovery partition, and deleted it.
3320- // 3. delete system.img and SWAP.img.
3321+ // 3. delete system.img
3322 try {
3323 int r = executeSUCommands(
3324 new String[]{
3325@@ -492,67 +674,86 @@
3326 Utils.getRecoveryPartitionPath()),
3327 (String.format("rm -f %s/%s || true\n", getFilesDir().toString(), ANDROID_REOCVERY_IMG)),
3328 ("rm -rf /data/system.img || true\n"),
3329- ("rm -rf /data/SWAP.img || true\n"),
3330 } );
3331
3332 if (r == 0) {
3333 // delete installed version in preferences
3334 cleanUpdateCommand();
3335- VersionInfo.storeEmptyVersion(
3336- getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE).edit(), PREF_KEY_INSTALLED_VERSION);
3337+ if (removeUserData) {
3338+ mSharedPreferences.edit()
3339+ .putInt( PREF_KEY_SIZE_SYSTEM_DATA, 0)
3340+ .putInt(PREF_KEY_SIZE_USER_DATA, 0)
3341+ .commit();
3342+ }
3343+ setUbuntuUninstalled();
3344 }
3345- result.putExtra("result", r);
3346 } catch (EShellExecException e) {
3347- result.putExtra("result", -1);
3348+ // ignore error
3349 }
3350-
3351- return result;
3352 }
3353
3354- private Intent doDeleteUbuntuUserData(Intent intent) {
3355- Intent result = new Intent(VERSION_UPDATE);
3356- File workingFolder = new File(mRootOfWorkPath, TEMP_FOLDER);
3357+ private void deleteUbuntuUserData() {
3358+ File workingFolder = new File(getFilesDir(), TEMP_FOLDER);
3359 File updateCommand = new File(workingFolder, UPDATE_COMMAND);
3360
3361 try {
3362- int r = executeSUCommands(new String[]{
3363- String.format("echo \"%s %s\" > %s\n", COMMAND_FORMAT, PARTITION_DATA, UPDATE_COMMAND),
3364+ mSharedPreferences.edit()
3365+ .putInt(PREF_KEY_SIZE_SYSTEM_DATA, 0)
3366+ .putInt(PREF_KEY_SIZE_USER_DATA, 0)
3367+ .commit();
3368+
3369+ executeSUCommands(new String[]{
3370+ String.format("echo \"%s %s\" > %s\n", COMMAND_FORMAT, PARTITION_DATA, updateCommand.getAbsolutePath()),
3371 ("sh " + UPDATE_SCRIPT + " " + updateCommand.getAbsolutePath() + " " + getFilesDir().toString() + "\n")
3372 } );
3373- result.putExtra("result", r);
3374 } catch (EShellExecException e) {
3375- result.putExtra("fail_description", e.getMessage());
3376- result.putExtra("result", -1);
3377+ Log.e(TAG,e.getMessage());
3378 }
3379- return result;
3380 }
3381
3382- private void doReboot(Intent intent) {
3383- // Reboot to recovery to complete update, try power manager if we have permissions
3384+ private void rebootToUbuntu() {
3385+ // update storage use before reboot
3386+ doUpdateStorageUse();
3387 try {
3388- PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
3389- powerManager.reboot("recovery");
3390- } catch (SecurityException e) {
3391- // FIXME: in Android 4.4, we do not get power manager permission.
3392- // try it with SU permissions
3393- try {
3394- int r = executeSUCommands(new String[] {
3395- String.format("%s -b %s/%s %s || true\n",
3396- ANDROID_BOOTMGR,
3397- getFilesDir().getAbsolutePath(),
3398- UBUNTU_BOOT_IMG,
3399- Utils.getRecoveryPartitionPath()),
3400- "reboot recovery\n"
3401- });
3402- if(r != 255) {
3403- Utils.showToast(this.getApplicationContext(), "Rebooting to Ubuntu");
3404- } else {
3405- Utils.showToast(this.getApplicationContext(), "No permissions to reboot to recovery");
3406- }
3407- } catch (EShellExecException e1) {
3408+ int r = executeSUCommands(new String[] {
3409+ String.format("%s -b %s/%s %s || true\n",
3410+ ANDROID_BOOTMGR,
3411+ getFilesDir().getAbsolutePath(),
3412+ UBUNTU_BOOT_IMG,
3413+ Utils.getRecoveryPartitionPath()),
3414+ "reboot recovery\n"
3415+ });
3416+ if(r != 255) {
3417+ Utils.showToast(this.getApplicationContext(), "Rebooting to Ubuntu");
3418+ } else {
3419 Utils.showToast(this.getApplicationContext(), "No permissions to reboot to recovery");
3420 }
3421- }
3422+ } catch (EShellExecException e1) {
3423+ Utils.showToast(this.getApplicationContext(), "No permissions to reboot to recovery");
3424+ }
3425+ }
3426+
3427+ /**
3428+ * Install back original recovery image
3429+ */
3430+ private void prepareAndroidUpdate() {
3431+ try {
3432+ int r = executeSUCommands(new String[] {
3433+ String.format("%s -b %s/%s %s || true\n",
3434+ ANDROID_BOOTMGR,
3435+ getFilesDir().getAbsolutePath(),
3436+ ANDROID_REOCVERY_IMG,
3437+ Utils.getRecoveryPartitionPath())
3438+ });
3439+ if(r != 255) {
3440+ Utils.showToast(this.getApplicationContext(), "Restoring original recovery for Android updates");
3441+ } else {
3442+ Utils.showToast(this.getApplicationContext(), "No permissions to restore recovery");
3443+ }
3444+ } catch (EShellExecException e1) {
3445+ Utils.showToast(this.getApplicationContext(), "No permissions to restore recovery");
3446+ }
3447+
3448 }
3449
3450 /**
3451@@ -563,16 +764,27 @@
3452 * @throws EShellExecException
3453 */
3454 private int executeSUCommands(String[] commands) throws EShellExecException {
3455+ mWakeLock.acquire(2 * 60 * 1000); // 2 minutes
3456+ try {
3457+ return doExecuteSUCommands(commands);
3458+ } finally {
3459+ if (mWakeLock.isHeld() ) {
3460+ mWakeLock.release();
3461+ }
3462+ Log.d(TAG, "executeSUCommands-releasing wakelocks");
3463+ }
3464+ }
3465+
3466+ private int doExecuteSUCommands(String[] commands) throws EShellExecException {
3467 int ret = 0;
3468- File rootFolder = new File(mRootOfWorkPath);
3469- File workingFolder = new File(rootFolder, TEMP_FOLDER);
3470+ File workingFolder = new File(getFilesDir(), TEMP_FOLDER);
3471 String workingFolderPath = workingFolder.getAbsolutePath();
3472
3473 if (!workingFolder.exists() && !workingFolder.mkdir()) {
3474 throw(new EShellExecException("Failed to create working folder"));
3475 }
3476
3477- broadcastProgress(0, "Extracting supporting files at " + workingFolder.getAbsolutePath());
3478+ broadcastProgress("Extracting supporting files at " + workingFolder.getAbsolutePath());
3479 try {
3480 // extract utils into working folder.
3481 Utils.extractExecutableAsset(this, ANDROID_BOOTMGR, workingFolderPath, true);
3482@@ -581,12 +793,10 @@
3483 Utils.extractExecutableAsset(this, GPG, workingFolderPath, true);
3484 Utils.extractExecutableAsset(this, TAR, workingFolderPath, true);
3485 Utils.extractExecutableAsset(this, UPDATE_SCRIPT, workingFolderPath, true);
3486- Utils.extractExecutableAsset(this, UPGRADECHECKER, workingFolderPath, true);
3487 } catch (IOException e) {
3488 throw(new EShellExecException("Failed to extract supporting utils"));
3489 }
3490
3491- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "shelling");
3492 try {
3493 Process process = Runtime.getRuntime().exec("su", null, workingFolder);
3494 DataOutputStream os = new DataOutputStream(process.getOutputStream());
3495@@ -615,6 +825,7 @@
3496 InputStream es = process.getErrorStream();
3497 boolean running = true;
3498 boolean scriptExecuted = false;
3499+ long time = System.currentTimeMillis();
3500 do {
3501 while( is.available() > 0) {
3502 read = is.read(buff);
3503@@ -623,30 +834,24 @@
3504 }
3505 scriptExecuted = true;
3506 String seg = new String(buff, 0, read);
3507- Log.i(TAG, "Script Output: " + seg);
3508- broadcastProgress(-1, seg);
3509+
3510+ Log.i(TAG, "Time(" + ((System.currentTimeMillis() - time)/1000) + "):Script Output: " + seg);
3511+ broadcastProgress(seg);
3512+ scanOutPut(seg);
3513 }
3514 while( es.available() > 0) {
3515 read = es.read(buff);
3516 if ( read <= 0 ) {
3517 break;
3518 }
3519- scriptExecuted = true;
3520- String seg = new String(buff,0,read);
3521-
3522- if (seg.startsWith("SWAP-file-missing")) {
3523- // this is signal that we will also install swap, adjust progress estimates
3524- mTotalSize += PROGRESS_MKSWAP_ADJUSTMENT + PROGRESS_SWAP_CREATION_ADJUSTMENT;
3525- } else {
3526- mProgress++;
3527- if (mTotalSize > 0 && mLastSignalledProgress < (mProgress * 100 / mTotalSize)) {
3528- // update and signal new progress
3529- mLastSignalledProgress = (int) (mProgress * 100 / mTotalSize);
3530- broadcastProgress(mLastSignalledProgress, null);
3531- }
3532+ scriptExecuted = true;
3533+ mProgress++;
3534+ if (mTotalSize > 0 && mLastSignalledProgress < (mProgress * 100 / mTotalSize)) {
3535+ // update and signal new progress
3536+ mLastSignalledProgress = (int) (mProgress * 100 / mTotalSize);
3537+ broadcastProgress(null);
3538 }
3539-
3540- Log.i(TAG, "Stderr Output: " + seg);
3541+ // Log.i(TAG, "Stderr Output: " + new String(buff,0,read));
3542 }
3543 try {
3544 ret = process.exitValue();
3545@@ -663,335 +868,467 @@
3546 } while (running);
3547 } catch (IOException e) {
3548 throw(new EShellExecException("Script execution exception " + e.getMessage()));
3549+ }
3550+ Log.d(TAG, "executeSUCommands-done");
3551+ return ret;
3552+ }
3553+
3554+ /**
3555+ * Scan output stream for additional information about installed system
3556+ * It scans for:
3557+ * - usage data statistics
3558+ * - installed version information
3559+ * - update channel url if needed
3560+ *
3561+ * @param seg
3562+ */
3563+ private void scanOutPut(String seg) {
3564+ for( String line : seg.split("\n")) {
3565+ if (line.endsWith(UBUNTU_ROOTFS)) {
3566+ storeStorageUse(line, PREF_KEY_SIZE_ROOTFS);
3567+ } else if (line.endsWith(UBUNTU_SYSTEM_DATA)) {
3568+ storeStorageUse(line, PREF_KEY_SIZE_SYSTEM_DATA);
3569+ } else if (line.endsWith(UBUNTU_USER_DATA)) {
3570+ storeStorageUse(line, PREF_KEY_SIZE_USER_DATA);
3571+ } else if (line.startsWith("build_number:")) {
3572+ parseVersion(line, PREF_KEY_VERSION_NUMBER);
3573+ } else if (line.startsWith("channel:")) {
3574+ parseVersion(line, PREF_KEY_VERSION_CHANNEL);
3575+ } else if (line.startsWith("channel_target:")) {
3576+ parseVersion(line, PREF_KEY_VERSION_CHANNEL_TARGET);
3577+ } else if (line.startsWith("version_detail:")) {
3578+ parseVersion(line, PREF_KEY_VERSION_DESCRIPTION);
3579+ } else if (line.startsWith("base:")) {
3580+ parseVersion(line, PREF_KEY_VERSION_BASE);
3581+ }
3582+ }
3583+ }
3584+
3585+ /**
3586+ * if channel address is empty, try to reconstruct it from version info
3587+ */
3588+ private void checkChannelAddress() {
3589+ if (!mSharedPreferences.contains(PREF_KEY_DOWNLOAD_CHANNEL)
3590+ || !mSharedPreferences.contains(PREF_KEY_DOWNLOAD_SERVER)){
3591+ final String base = mSharedPreferences.getString(PREF_KEY_VERSION_BASE, "");
3592+ String channel = mSharedPreferences.getString(PREF_KEY_VERSION_CHANNEL, "");
3593+ // if we have network, try to ping server, otherwise guess
3594+ String channelUrl = null;
3595+ if (isNetworkAvailable()) {
3596+ channelUrl = doGetChannels(String.format("http://%s", base), false, true, true, true).get(channel);
3597+ }
3598+
3599+ if (channelUrl == null) {
3600+ // guess name
3601+ if (channel.contains("ubuntu-touch/")) {
3602+ channelUrl = String.format("%s/%s/index.json", channel, Utils.getDeviceModel());
3603+ } else {
3604+ channelUrl = String.format("ubuntu-touch/%s/%s/index.json", channel, Utils.getDeviceModel());
3605+ }
3606+ }
3607+ Log.d(TAG, "Constructed channel url:" + base + "/" + channelUrl);
3608+ mSharedPreferences.edit()
3609+ .putString(PREF_KEY_DOWNLOAD_CHANNEL, channelUrl)
3610+ .putString(PREF_KEY_DOWNLOAD_SERVER, String.format("http://%s", base))
3611+ .commit();
3612+ }
3613+ }
3614+
3615+ private void storeStorageUse(String value, String key) {
3616+ final String[] spl = value.split("\t");
3617+ if (spl.length != 0) {
3618+ final int size = Integer.valueOf(spl[0]);
3619+ mSharedPreferences.edit().putInt(key, size).commit();
3620+ final Intent notification = new Intent(STORAGE_USE_UPDATED);
3621+ sendBroadcast(notification);
3622+ }
3623+ }
3624+
3625+ private void parseVersion(String value, String key) {
3626+ final String[] spl = value.split(": ");
3627+ if (spl.length >= 2) {
3628+ mSharedPreferences.edit().putString(key, spl[1]).commit();
3629+ }
3630+ }
3631+
3632+ private Intent downloadRelease(Intent intent) {
3633+ String channel = intent.getStringExtra(DOWNLOAD_RELEASE_EXTRA_CHANNEL_URL);
3634+ String serverUrl = intent.getStringExtra(DOWNLOAD_RELEASE_EXTRA_SERVER_URL);
3635+ boolean bootstrap = intent.getBooleanExtra(DOWNLOAD_RELEASE_EXTRA_BOOTSTRAP,true); // default bootstrap on
3636+ int version = intent.getIntExtra(DOWNLOAD_RELEASE_EXTRA_VERSION, -1);
3637+ ReleaseType releaseType = ReleaseType.fromValue(
3638+ intent.getIntExtra(DOWNLOAD_RELEASE_EXTRA_TYPE, ReleaseType.FULL.getValue())); // by default look for full releases
3639+ // First delete old release if it exists
3640+ doDeleteDownloadedRelease();
3641+ mSharedPreferences.edit().
3642+ putString(PREF_KEY_DOWNLOAD_SERVER, serverUrl).
3643+ putString(PREF_KEY_DOWNLOAD_CHANNEL, channel).
3644+ commit();
3645+ return downloadAllPackages(bootstrap, version, releaseType);
3646+ }
3647+
3648+ /**
3649+ * Download first available download
3650+ */
3651+ private Intent downloadUpdate() {
3652+ // -1 will pick update available for this release
3653+ return downloadAllPackages(false, -1, ReleaseType.DELTA);
3654+ }
3655+
3656+ private Intent resumeDownload() {
3657+ boolean bootstrap = mSharedPreferences.getBoolean(PREF_KEY_DOWNLOAD_BOOTSTRAP, false);
3658+ int version = mSharedPreferences.getInt(PREF_KEY_DOWNLOAD_VERSION, -1);
3659+ ReleaseType releaseType = ReleaseType.fromValue(
3660+ mSharedPreferences.getInt(PREF_KEY_DOWNLOAD_TYPE, ReleaseType.FULL.getValue()));
3661+ return downloadAllPackages(bootstrap, version, releaseType);
3662+ }
3663+
3664+ @SuppressLint("Wakelock")
3665+ private Intent downloadAllPackages(boolean bootstrap, int version, ReleaseType releaseType ) {
3666+ mWakeLock.acquire();
3667+ mWifiLock.acquire();
3668+ try {
3669+ return doDownloadAllPackages( bootstrap, version, releaseType);
3670 } finally {
3671- if (mWakeLock != null && mWakeLock.isHeld()) {
3672+ Log.d(TAG, "downloadAllPackages-releasing wakelocks");
3673+ if (mWakeLock.isHeld() ) {
3674 mWakeLock.release();
3675 }
3676+ if (mWifiLock.isHeld()) {
3677+ mWifiLock.release();
3678+ }
3679 }
3680- return ret;
3681 }
3682-
3683- private Intent doDownloadRelease(Intent intent) {
3684- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ufa-downloading");
3685+ @SuppressLint("CommitPrefEdits")
3686+ private Intent doDownloadAllPackages(boolean bootstrap, int version, ReleaseType releaseType ) {
3687 mIsCanceled = false;
3688- SharedPreferences.Editor editor = getSharedPreferences( SHARED_PREF, Context.MODE_PRIVATE).edit();
3689-
3690+ dismissUbuntuNotification();
3691+ mActionOutput = "";
3692+ broadcastProgress(getString(R.string.downloading_starting));
3693+ String channel = mSharedPreferences.getString(PREF_KEY_DOWNLOAD_CHANNEL, null);
3694+ String serverUrl = mSharedPreferences.getString(PREF_KEY_DOWNLOAD_SERVER, null);
3695 Intent result = new Intent(DOWNLOAD_RESULT);
3696- VersionInfo prevDownload = getDownloadVersion(this.getApplicationContext());
3697- try {
3698- File rootFolder = new File(mRootOfWorkPath);
3699-
3700- // first get from JSON list of files to download
3701- String alias = intent.getStringExtra(DOWNLOAD_RELEASE_EXTRA_CHANNEL_ALIAS);
3702- String jsonUrl = intent.getStringExtra(DOWNLOAD_RELEASE_EXTRA_CHANNEL_URL);
3703- boolean bootstrap = intent.getBooleanExtra(DOWNLOAD_RELEASE_EXTRA_BOOTSTRAP,true); // default bootstrap on
3704- int version = intent.getIntExtra(DOWNLOAD_RELEASE_EXTRA_VERSION, -1);
3705- ReleaseType releaseType = ReleaseType.fromValue(
3706- intent.getIntExtra(DOWNLOAD_RELEASE_EXTRA_TYPE, ReleaseType.FULL.getValue())); // by default look for full releases
3707-
3708- String jsonStr = Utils.httpDownload(BASE_URL + jsonUrl);
3709- List<Image> releases = JsonChannelParser.getAvailableReleases(jsonStr, ReleaseType.FULL);
3710- if (releases.size() == 0 || releases.get(0).files.length == 0 ) {
3711- // something is wrong, empty release
3712- Log.e(TAG, "Empty releas");
3713- return handleDownloadError(result, -1, "Empty release");
3714+ if (channel == null || serverUrl == null) {
3715+ doDeleteDownloadedRelease();
3716+ return handleDownloadError(result, -1, "Missing update server or channel url");
3717+ }
3718+
3719+ String jsonStr = Utils.httpDownload(String.format("%s/%s", serverUrl, channel));
3720+ if (jsonStr == null) {
3721+ Log.e(TAG, "Failed to fetch channel content");
3722+ return handleDownloadError(result, -1, "Failed to fetch channel content, check url");
3723+ }
3724+ List<Image> releases = JsonChannelParser.getAvailableReleases(jsonStr, releaseType);
3725+ if (releases.size() == 0 || releases.get(0).files.length == 0 ) {
3726+ // something is wrong, empty release
3727+ Log.e(TAG, "Empty releas");
3728+ return handleDownloadError(result, -1, "Empty release");
3729+ }
3730+ // get right version, otherwise first since that is most recent one
3731+ Image choosenRelease = null;
3732+ if (version != -1) {
3733+ // look for given release
3734+ for (Image i : releases) {
3735+ if (i.version == version) {
3736+ choosenRelease = i;
3737+ break;
3738+ }
3739 }
3740- // get right version, otherwise first since that is most recent one
3741- Image choosenRelease = null;
3742- if (version != -1) {
3743- // look for given release
3744- for (Image i : releases) {
3745- if (i.version == version) {
3746+ } else {
3747+ if (releaseType == ReleaseType.FULL) {
3748+ choosenRelease = releases.get(0);
3749+ } else {
3750+ // this is update, found release wich relates to installed one
3751+ String baseStr = mSharedPreferences.getString(PREF_KEY_VERSION_NUMBER, "0");
3752+ int base = Integer.valueOf(baseStr);
3753+ for(Image i : releases) {
3754+ if (i.base == base) {
3755 choosenRelease = i;
3756 break;
3757 }
3758 }
3759- if (choosenRelease == null) {
3760- Log.e(TAG, "wrong release vwersion");
3761- return handleDownloadError(result, -1, "wrong release vwersion");
3762+ }
3763+ }
3764+ if (choosenRelease == null) {
3765+ Log.e(TAG, "wrong release vwersion");
3766+ return handleDownloadError(result, -1, "wrong release version");
3767+ }
3768+
3769+ JsonChannelParser.File updateFiles[] = choosenRelease.files;
3770+ // sort update files
3771+ List<JsonChannelParser.File> filesArray = new LinkedList<JsonChannelParser.File>();
3772+ for(JsonChannelParser.File f: updateFiles) {
3773+ // In bring up mode, skip device package updates
3774+ if (mBringup) {
3775+ if (!f.path.contains("device-")){
3776+ filesArray.add(f);
3777 }
3778 } else {
3779- choosenRelease = releases.get(0);
3780- }
3781- JsonChannelParser.File updateFiles[] = choosenRelease.files;
3782- // sort update files
3783- List<JsonChannelParser.File> filesArray = new LinkedList<JsonChannelParser.File>();
3784- for(JsonChannelParser.File f: updateFiles) {
3785 filesArray.add(f);
3786 }
3787- Collections.sort(filesArray, JsonChannelParser.fileComparator());
3788- String updateFilenames[] = new String[updateFiles.length * 2];
3789-
3790- // get list of keyrings to download
3791- String keyrings[] = {
3792- String.format("%s/%s",BASE_URL, URL_IMAGE_MASTER),
3793- String.format("%s/%s",BASE_URL, URL_IMAGE_SIGNING),
3794- };
3795- String keyringsFilenames[] = new String[keyrings.length * 2];
3796-
3797- // First delete old release if it exists
3798- {
3799- boolean toDeleteOld = true;
3800- if (prevDownload != null) {
3801- if (prevDownload.equals(jsonUrl, choosenRelease.version, releaseType) &&
3802- prevDownload.mDownloadedSize > 0) {
3803- toDeleteOld = false;
3804- }
3805- }
3806- if (toDeleteOld) {
3807- String s = deleteRelease();
3808- if (s != null) {
3809- // remove failed
3810- return handleDownloadError(result, -1, s);
3811- }
3812- }
3813- }
3814-
3815- // make sure release folder exists
3816- File release = new File(rootFolder, RELEASE_FOLDER);
3817- release.mkdir();
3818- // download release
3819- long time = System.currentTimeMillis();
3820- mLastSignalledProgress = 0;
3821- mProgress = 0;
3822- broadcastProgress(mLastSignalledProgress, null);
3823- mTotalSize = Utils.calculateDownloadSize(filesArray);
3824- long neededSize = mTotalSize;
3825- if (prevDownload != null) {
3826- neededSize = mTotalSize - prevDownload.mDownloadedSize;
3827- }
3828- boolean isStorageEnough = isStorageSpaceEnoughBFDownload(neededSize);
3829- if (! isStorageEnough) {
3830- String msg = "Need more storage: ";
3831- if (workPathInCache) {
3832- msg += "/cache need " + String.valueOf(mTotalSize) + " bytes for download and /data need 2.5G for system";
3833+ }
3834+ Collections.sort(filesArray, JsonChannelParser.fileComparator());
3835+ String updateFilenames[] = new String[updateFiles.length];
3836+
3837+ // make sure release folder exists
3838+ mReleaseFolder.mkdir();
3839+ // download release
3840+ long time = System.currentTimeMillis();
3841+ mLastSignalledProgress = 0;
3842+ mProgress = 0;
3843+ broadcastProgress(null);
3844+ mTotalSize = Utils.calculateDownloadSize(filesArray);
3845+ long neededSize = mTotalSize;
3846+ boolean resuming = mSharedPreferences.getBoolean(PREF_KEY_DOWNLOAD_ACTIVE, false);
3847+ if (resuming ) {
3848+ neededSize = mTotalSize - mSharedPreferences.getLong(PREF_KEY_DOWNLOAD_COMPLETED, 0);
3849+ }
3850+ boolean isStorageEnough = isStorageSpaceEnoughBFDownload(neededSize);
3851+ if (! isStorageEnough) {
3852+ String msg = "Need more storage: ";
3853+ msg += "/data need 2.5G for system plus " + String.valueOf(mTotalSize) + " bytes for download";
3854+ Log.i(TAG, msg);
3855+ return handleDownloadError(result, -1, msg);
3856+ }
3857+
3858+ // Store download info
3859+ mSharedPreferences.edit()
3860+ .putBoolean(PREF_KEY_DOWNLOAD_BOOTSTRAP, bootstrap)
3861+ .putInt(PREF_KEY_DOWNLOAD_VERSION, version)
3862+ .putInt(PREF_KEY_DOWNLOAD_TYPE, releaseType.getValue())
3863+ .putBoolean(PREF_KEY_DOWNLOAD_ACTIVE, true)
3864+ .putString(PREF_KEY_DOWNLOAD_SERVER, serverUrl)
3865+ .putString(PREF_KEY_DOWNLOAD_CHANNEL, channel)
3866+ .commit();
3867+
3868+ // mProgressSteps = mTotalDownloadSize / 100; // we want 1% steps
3869+ long downloadedSize = 0;
3870+ JsonChannelParser.File currentDownloadingFile = null;
3871+ try {
3872+ int i = 0;
3873+ // download all update packages
3874+ i = 0;
3875+ for (JsonChannelParser.File file : filesArray){
3876+ URL url = new URL(serverUrl + file.path);
3877+ String fileName = URLUtil.guessFileName(url.toString(), null, null);
3878+ File f = new File(mReleaseFolder, fileName);
3879+
3880+ boolean fileNeedDownload = true;
3881+ if (resuming) {
3882+ long length = f.length();
3883+ if (length == file.size) {
3884+ fileNeedDownload = false;
3885+ } else if (length > file.size) {
3886+ f.delete();
3887+ }
3888+ }
3889+ if (fileNeedDownload) {
3890+ currentDownloadingFile = file;
3891+ updateFilenames[i] = doDownloadFile(serverUrl, file, mReleaseFolder);
3892+ currentDownloadingFile = null;
3893 } else {
3894- msg += "/data need 2.5G for system plus " + String.valueOf(mTotalSize) + " bytes for download";
3895- }
3896- Log.i(TAG, msg);
3897- return handleDownloadError(result, -1, msg);
3898- }
3899-
3900- // mProgressSteps = mTotalDownloadSize / 100; // we want 1% steps
3901- long downloadedSize = 0;
3902- JsonChannelParser.File currentDownloadingFile = null;
3903+ updateFilenames[i] = fileName;
3904+ mProgress += file.size;
3905+ mLastSignalledProgress = (int) (mProgress * 100 / mTotalSize);
3906+ broadcastProgress(null);
3907+ }
3908+
3909+ // check file size and check sum
3910+ long length = f.length();
3911+ if (length != file.size) {
3912+ f.delete();
3913+ throw new ESumNotMatchException();
3914+ }
3915+ broadcastProgress("Checksum Verifying: " + fileName);
3916+ String sha256sum = Utils.getSha256Sum(f);
3917+ if (! sha256sum.equals(file.checksum)) {
3918+ broadcastProgress("Checksum Verify failed: " + fileName);
3919+ f.delete();
3920+ throw new ESumNotMatchException();
3921+ }
3922+ broadcastProgress("Checksum Verified: " + fileName);
3923+ downloadedSize += file.size;
3924+ i++;
3925+ }
3926+
3927+ // data required for installation.
3928+ // Ubuntu reboot apps.
3929+ Utils.extractExecutableAsset(this, U_REBOOT_APP, mReleaseFolder.getAbsolutePath(), false);
3930+ Utils.extractExecutableAsset(this, RECOVERY_REMOUNT, mReleaseFolder.getAbsolutePath(), false);
3931+ } catch (MalformedURLException e) {
3932+ Log.e(TAG, "Failed to download release:", e);
3933+ return handleDownloadError(result, -1, "Malformed release url");
3934+ } catch (FileNotFoundException e) {
3935+ Log.e(TAG, "Failed to download release:", e);
3936+ return handleDownloadError(result, -1, "File not found");
3937+ } catch (IOException e){
3938+ if (currentDownloadingFile != null) {
3939+ try {
3940+ URL url = new URL(serverUrl + currentDownloadingFile.path);
3941+ String fileName = URLUtil.guessFileName(url.toString(), null, null);
3942+ File f = new File(mReleaseFolder, fileName);
3943+ downloadedSize += f.length();
3944+ } catch (MalformedURLException e1) {
3945+ // shouldn't happen for it should already happen.
3946+ }
3947+ }
3948+
3949+ if (downloadedSize == 0) {
3950+ doDeleteDownloadedRelease();
3951+ mSharedPreferences.edit().putBoolean(PREF_KEY_DOWNLOAD_ACTIVE, false).commit();
3952+ } else {
3953+ mSharedPreferences.edit().putLong(PREF_KEY_DOWNLOAD_COMPLETED, downloadedSize).commit();
3954+ }
3955+
3956+ Log.e(TAG, "Failed to download release:", e);
3957+ return handleDownloadError(result, -1, "IO Error");
3958+ } catch (ESumNotMatchException e) {
3959+ // Download file check sum error !!
3960+ doDeleteDownloadedRelease();
3961+ return handleDownloadError(result, -1, "Download check sum error");
3962+ } catch (ECancelException e) {
3963+ // Download was cancelled by user
3964+ downloadedSize += e.mDownloadedSize;
3965+ if (downloadedSize == 0) {
3966+ mSharedPreferences.edit().putBoolean(PREF_KEY_DOWNLOAD_ACTIVE, false).commit();
3967+ } else {
3968+ mSharedPreferences.edit().putLong(PREF_KEY_DOWNLOAD_COMPLETED, downloadedSize).commit();
3969+ }
3970+
3971+ return handleDownloadError(result, -2, "Download cancelled by user");
3972+ }
3973+
3974+ Log.i(TAG, "Download done in " + (System.currentTimeMillis() - time )/1000 + " seconds");
3975+ broadcastProgress("Generating update command");
3976+
3977+ // generate update_command
3978+ File updateCommand = new File(mReleaseFolder, UPDATE_COMMAND);
3979+ try {
3980+ FileOutputStream fos = new FileOutputStream(updateCommand);
3981 try {
3982+ if (bootstrap) {
3983+ fos.write((String.format("%s %s\n", COMMAND_FORMAT, PARTITION_DATA)).getBytes());
3984+ }
3985+ if (releaseType == ReleaseType.FULL) {
3986+ fos.write((String.format("%s %s\n", COMMAND_FORMAT, PARTITION_SYSTEM)).getBytes());
3987+ }
3988+ fos.write((String.format("%s %s\n", COMMAND_MOUNT, PARTITION_SYSTEM)).getBytes());
3989+
3990+ // add update commands
3991 int i = 0;
3992- for(String url : keyrings){
3993- keyringsFilenames[i++] = doDownloadUrl(new URL(url), release);
3994- // download signature
3995- keyringsFilenames[i++] = doDownloadUrl(new URL(url+ASC_SUFFIX), release);
3996- }
3997-
3998- // download all update images
3999- i = 0;
4000- for (JsonChannelParser.File file : filesArray){
4001- URL url = new URL(BASE_URL + file.path);
4002- String fileName = URLUtil.guessFileName(url.toString(), null, null);
4003- File f = new File(release, fileName);
4004-
4005- boolean fileNeedDownload = true;
4006- if (prevDownload != null) {
4007- long length = f.length();
4008- if (length == file.size) {
4009- fileNeedDownload = false;
4010- } else if (length > file.size) {
4011- f.delete();
4012- }
4013- }
4014- if (fileNeedDownload) {
4015- currentDownloadingFile = file;
4016- updateFilenames[i] = doDownloadFile(file, release);
4017- currentDownloadingFile = null;
4018- } else {
4019- updateFilenames[i] = fileName;
4020- mProgress += file.size;
4021- mLastSignalledProgress = (int) (mProgress * 100 / mTotalSize);
4022- broadcastProgress(mLastSignalledProgress, null);
4023- }
4024-
4025- // check file size and check sum
4026- long length = f.length();
4027- if (length != file.size) {
4028- f.delete();
4029- throw new ESumNotMatchException();
4030- }
4031- broadcastProgress(mLastSignalledProgress, "Checksum Verifying: " + fileName);
4032- String sha256sum = Utils.getSha256Sum(f);
4033- if (! sha256sum.equals(file.checksum)) {
4034- broadcastProgress(mLastSignalledProgress, "Checksum Verify failed: " + fileName);
4035- f.delete();
4036- throw new ESumNotMatchException();
4037- }
4038- broadcastProgress(mLastSignalledProgress, "Checksum Verified: " + fileName);
4039- downloadedSize += file.size;
4040- i++;
4041-
4042- // signature file size is not accounted for resume since it's quite small
4043- updateFilenames[i] = doDownloadFileSignature(file, release);
4044- i++;
4045- }
4046-
4047- // data required for installation.
4048- Utils.extractExecutableAsset(this, ARCHIVE_MASTER_ASC, release.getAbsolutePath(), false);
4049- Utils.extractExecutableAsset(this, ARCHIVE_MASTER, release.getAbsolutePath(), false);
4050- // Ubuntu reboot apps.
4051- Utils.extractExecutableAsset(this, U_REBOOT_APP_ASC, release.getAbsolutePath(), false);
4052- Utils.extractExecutableAsset(this, U_REBOOT_APP, release.getAbsolutePath(), false);
4053- } catch (MalformedURLException e) {
4054- Log.e(TAG, "Failed to download release:", e);
4055- return handleDownloadError(result, -1, "Malformed release url");
4056- } catch (FileNotFoundException e) {
4057- Log.e(TAG, "Failed to download release:", e);
4058- return handleDownloadError(result, -1, "File not found");
4059- } catch (IOException e){
4060- if (currentDownloadingFile != null) {
4061- try {
4062- URL url = new URL(BASE_URL + currentDownloadingFile.path);
4063- String fileName = URLUtil.guessFileName(url.toString(), null, null);
4064- File f = new File(release, fileName);
4065- downloadedSize += f.length();
4066- } catch (MalformedURLException e1) {
4067- // shouldn't happen for it should already happen.
4068- }
4069- }
4070-
4071- if (downloadedSize > 0) {
4072- VersionInfo v = new VersionInfo(alias, jsonUrl, choosenRelease.description, choosenRelease.version,
4073- downloadedSize, releaseType);
4074- v.storeVersion(editor, PREF_KEY_DOWNLOADED_VERSION);
4075- } else {
4076- editor.putString(PREF_KEY_UPDATE_COMMAND, "");
4077- VersionInfo.storeEmptyVersion(editor, PREF_KEY_DOWNLOADED_VERSION);
4078- }
4079-
4080- Log.e(TAG, "Failed to download release:", e);
4081- return handleDownloadError(result, -1, "IO Error");
4082- } catch (ESumNotMatchException e) {
4083- // Download file check sum error !!
4084- return handleDownloadError(result, -1, "Download check sum error");
4085- } catch (ECancelException e) {
4086- // Download was cancelled by user
4087- downloadedSize += e.mDownloadedSize;
4088- if (downloadedSize > 0) {
4089- VersionInfo v = new VersionInfo(alias, jsonUrl, choosenRelease.description, choosenRelease.version,
4090- downloadedSize, releaseType);
4091- v.storeVersion(editor, PREF_KEY_DOWNLOADED_VERSION);
4092- }
4093- return handleDownloadError(result, -2, "Download cancelled by user");
4094- }
4095-
4096- Log.i(TAG, "Download done in " + (System.currentTimeMillis() - time )/1000 + " seconds");
4097- broadcastProgress(-1, "Generating update command");
4098-
4099- // generate update_command
4100- File updateCommand = new File(release, UPDATE_COMMAND);
4101- try {
4102- FileOutputStream fos = new FileOutputStream(updateCommand);
4103- try {
4104- if (bootstrap) {
4105- fos.write((String.format("%s %s\n", COMMAND_FORMAT, PARTITION_DATA)).getBytes());
4106- }
4107- if (releaseType == ReleaseType.FULL) {
4108- fos.write((String.format("%s %s\n", COMMAND_FORMAT, PARTITION_SYSTEM)).getBytes());
4109- }
4110- // load keyrings
4111- int i = 0;
4112- while (i < keyringsFilenames.length) {
4113- fos.write((String.format("%s %s %s\n",
4114- COMMAND_LOAD_KEYRING,
4115- keyringsFilenames[i++],
4116- keyringsFilenames[i++])).getBytes());
4117- }
4118- fos.write((String.format("%s %s\n", COMMAND_MOUNT, PARTITION_SYSTEM)).getBytes());
4119-
4120- // add update commands
4121- i = 0;
4122- while (i < updateFilenames.length) {
4123- fos.write((String.format("%s %s %s\n",
4124- COMMAND_UPDATE,
4125- updateFilenames[i++],
4126- updateFilenames[i++])).getBytes());
4127- }
4128-
4129- // add Ubuntu reboot app update package
4130- if (releaseType == ReleaseType.FULL) {
4131- fos.write((String.format("%s %s %s\n",
4132- COMMAND_UPDATE,
4133- U_REBOOT_APP,
4134- U_REBOOT_APP_ASC)).getBytes());
4135- }
4136- if(releaseType == ReleaseType.DELTA) {
4137- // TODO:
4138- }
4139- fos.write((String.format("%s %s\n", COMMAND_UMOUNT, PARTITION_SYSTEM)).getBytes());
4140- fos.flush();
4141- } finally {
4142- fos.close();
4143- }
4144- } catch (IOException e) {
4145- e.printStackTrace();
4146- return handleDownloadError(result, -1, "Failed to generate update command");
4147- }
4148- broadcastProgress(-1, "Download done in " + (System.currentTimeMillis() - time )/1000 + " seconds");
4149- int estimatedCheckCount = 0;
4150- for (JsonChannelParser.File file : filesArray){
4151- if (file.path.contains("ubuntu-")) {
4152- estimatedCheckCount += (file.size / PROGRESS_UBUNTU_ADJUSTMENT);
4153- } else if (file.path.contains("device-")) {
4154- estimatedCheckCount += (file.size / PROGRESS_DEVICE_ADJUSTMENT);
4155- } else if (file.path.contains("custom-")) {
4156- estimatedCheckCount += (file.size / PROGRESS_CUSTOM_ADJUSTMENT);
4157- }
4158- }
4159- // store update command
4160- setUpdateCommand(updateCommand.getAbsolutePath());
4161-
4162- // updated downloaded information.
4163- VersionInfo v = new VersionInfo(alias, jsonUrl, choosenRelease.description, choosenRelease.version, 0, releaseType);
4164-
4165- editor.putInt(PREF_KEY_ESTIMATED_CHECKPOINTS, estimatedCheckCount);
4166- v.storeVersion(editor, PREF_KEY_DOWNLOADED_VERSION);
4167- mProgress = 100;
4168- } finally {
4169- if (mWakeLock != null && mWakeLock.isHeld()) {
4170- mWakeLock.release();
4171- }
4172+ while (i < updateFilenames.length) {
4173+ fos.write((String.format("%s %s\n",
4174+ COMMAND_UPDATE,
4175+ updateFilenames[i++])).getBytes());
4176+ }
4177+
4178+ // add Ubuntu reboot app and recovery remount update packages
4179+ if (releaseType == ReleaseType.FULL) {
4180+ fos.write((String.format("%s %s\n",
4181+ COMMAND_UPDATE,
4182+ U_REBOOT_APP)).getBytes());
4183+
4184+ fos.write((String.format("%s %s\n",
4185+ COMMAND_UPDATE,
4186+ RECOVERY_REMOUNT)).getBytes());
4187+ }
4188+ fos.write((String.format("%s %s\n", COMMAND_UMOUNT, PARTITION_SYSTEM)).getBytes());
4189+ fos.flush();
4190+ } finally {
4191+ fos.close();
4192+ }
4193+ } catch (IOException e) {
4194+ e.printStackTrace();
4195+ doDeleteDownloadedRelease();
4196+ mSharedPreferences.edit().putBoolean(PREF_KEY_DOWNLOAD_ACTIVE, false).commit();
4197+ return handleDownloadError(result, -1, "Failed to generate update command");
4198+ }
4199+
4200+ mLastSignalledProgress = 100;
4201+ broadcastProgress("Download done in " + (System.currentTimeMillis() - time )/1000 + " seconds");
4202+ // store update command
4203+ setUpdateCommand(updateCommand.getAbsolutePath());
4204+ mSharedPreferences.edit().putBoolean(PREF_KEY_DOWNLOAD_ACTIVE, false).commit();
4205+ dismissProgressNotification();
4206+ // check if we should also do install
4207+ if ( MIN_INSTALL_BATTERY_CHARGE < getBatteryCharge(this)) {
4208+ updateInstallerState(InstallerState.INSTALLING);
4209+ return doInstallUbuntu();
4210 }
4211 result.putExtra(DOWNLOAD_RESULT_EXTRA_INT, 0);
4212 return result;
4213 }
4214
4215+ /**
4216+ * Go through update folder and based on size estimate number of install checkpoint
4217+ * @param updateCommand update command file
4218+ * @return number of checkpoints
4219+ */
4220+ private int estimateCheckpooint(String updateCommand) {
4221+
4222+ long estimatedCheckCount = 0;
4223+ ReleaseType releaseType = ReleaseType.fromValue( // use delta as default for unknown updates
4224+ mSharedPreferences.getInt(PREF_KEY_DOWNLOAD_TYPE, ReleaseType.DELTA.getValue()));
4225+ File command = new File(updateCommand);
4226+ File updateFolder = command.getParentFile();
4227+ if (updateFolder != null && updateFolder.isDirectory()) {
4228+ // looks for any file in this folder
4229+ File[] files = updateFolder.listFiles(
4230+ new FileFilter(){
4231+ public boolean accept (File pathname) {
4232+ if (pathname.isFile() && pathname.getName().endsWith(".tar.xz")) {
4233+ return true;
4234+ }
4235+ return false;
4236+ }
4237+ });
4238+ for (File f : files) {
4239+ if (f.getName().startsWith("ubuntu-")) {
4240+ estimatedCheckCount += (f.length() / PROGRESS_UBUNTU_ADJUSTMENT);
4241+ } else if (f.getName().startsWith("device-")) {
4242+ estimatedCheckCount += (f.length() / PROGRESS_DEVICE_ADJUSTMENT);
4243+ } else {
4244+ estimatedCheckCount += (f.length() / PROGRESS_CUSTOM_ADJUSTMENT);
4245+ }
4246+ }
4247+ }
4248+ if (releaseType == ReleaseType.DELTA) {
4249+ // set double checkpoints for removing files
4250+ estimatedCheckCount *=2;
4251+ }
4252+ return (int)estimatedCheckCount;
4253+ }
4254+
4255+ /**
4256+ * Handle download error
4257+ */
4258 private Intent handleDownloadError(Intent i, int res, String reason) {
4259+ mActionOutput += "\n" + reason;
4260+ if ( res == -2) {
4261+ // download canceled by user, dismiss progress notification
4262+ dismissProgressNotification();
4263+ } else if (isUpdate()) {
4264+ showUbuntuNotification(R.string.notification_download_failed, LaunchActivity.class);
4265+ } else {
4266+ showUbuntuNotification(R.string.notification_download_failed, InstallActivity.class);
4267+ }
4268 i.putExtra(DOWNLOAD_RESULT_EXTRA_INT, res);
4269 i.putExtra(DOWNLOAD_RESULT_EXTRA_STR, reason);
4270 return i;
4271 }
4272
4273- private String doDownloadFile(JsonChannelParser.File file, File targetLocation) throws MalformedURLException,
4274- FileNotFoundException, IOException, ECancelException {
4275- URL url = new URL(BASE_URL + file.path);
4276+ private String doDownloadFile(String serverUrl, JsonChannelParser.File file, File targetLocation)
4277+ throws MalformedURLException, FileNotFoundException, IOException, ECancelException {
4278+ URL url = new URL(serverUrl + file.path);
4279 return doDownloadUrl(url, targetLocation, true);
4280 }
4281
4282- private String doDownloadFileSignature(JsonChannelParser.File file, File targetLocation) throws MalformedURLException,
4283- FileNotFoundException, IOException, ECancelException {
4284- URL url = new URL(BASE_URL + file.signature);
4285- return doDownloadUrl(url, targetLocation);
4286- }
4287-
4288- private String doDownloadUrl(URL url, File targertLocation) throws MalformedURLException,
4289- FileNotFoundException, IOException, ECancelException {
4290- return doDownloadUrl(url, targertLocation, false);
4291- }
4292-
4293 private String doDownloadUrl(URL url, File targertLocation, boolean resume) throws MalformedURLException,
4294 FileNotFoundException, IOException, ECancelException {
4295 Log.v(TAG, "Downloading:" + url.toString());
4296 URLConnection conn = url.openConnection();
4297 String fileName = URLUtil.guessFileName(url.toString(), null, null);
4298- // TODO: update progress accordingly
4299- broadcastProgress(mLastSignalledProgress, "Downloading: " + fileName);
4300+ broadcastProgress("Downloading: " + fileName);
4301 File file = new File(targertLocation, fileName);
4302 if ((! resume) && file.exists() && file.isFile()) {
4303 file.delete();
4304@@ -1011,7 +1348,7 @@
4305 // resumePosition > 0 ==> append mode
4306 FileOutputStream output = new FileOutputStream(file, resumePosition > 0);
4307
4308- InputStream input = conn.getInputStream();
4309+ final InputStream input = conn.getInputStream();
4310
4311 byte[] buffer = new byte[1024];
4312 int len = 0;
4313@@ -1021,9 +1358,18 @@
4314 output.close();
4315 conn = null;
4316 // input.close() need more time to close() as donwload large file.
4317- input = null;
4318+ new Thread(new Runnable() {
4319+ @Override
4320+ public void run() {
4321+ try {
4322+ input.close();
4323+ } catch (IOException e) {
4324+ e.printStackTrace();
4325+ }
4326+ }
4327+ }).start();
4328 long flen = file.length();
4329- if (flen > 0) {
4330+ if (flen > 0 && !resume) {
4331 try {
4332 file.delete();
4333 flen = 0;
4334@@ -1041,7 +1387,7 @@
4335 if (mLastSignalledProgress < (mProgress * 100 / mTotalSize)) {
4336 // update and signal new progress
4337 mLastSignalledProgress = (int) (mProgress * 100 / mTotalSize);
4338- broadcastProgress(mLastSignalledProgress, null);
4339+ broadcastProgress(null);
4340 }
4341 }
4342 output.flush();
4343@@ -1066,52 +1412,42 @@
4344 }
4345
4346 /**
4347- * @return null if success or error
4348+ * Delete downloaded release
4349 */
4350- private String deleteRelease() {
4351- // First delete old release if it exists
4352- File rootFolder = new File(mRootOfWorkPath);
4353- File release = new File(rootFolder, RELEASE_FOLDER);
4354- if (release.exists()) {
4355- deleteDirectory(release);
4356- // cleanup update command
4357- cleanUpdateCommand();
4358- // clean up version number.
4359- VersionInfo.storeEmptyVersion(getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE).edit(), PREF_KEY_DOWNLOADED_VERSION);
4360+ private void doDeleteDownloadedRelease() {
4361+ // delete old release if it exists
4362+ if (mReleaseFolder.exists()) {
4363+ deleteDirectory(mReleaseFolder);
4364 }
4365- return null;
4366+ // cleanup update command
4367+ cleanUpdateCommand();
4368+ mSharedPreferences.edit()
4369+ .remove(PREF_KEY_DOWNLOAD_CHANNEL)
4370+ .remove(PREF_KEY_DOWNLOAD_SERVER)
4371+ .remove(PREF_KEY_DOWNLOAD_BOOTSTRAP)
4372+ .remove(PREF_KEY_DOWNLOAD_VERSION)
4373+ .putBoolean(PREF_KEY_DOWNLOAD_ACTIVE, false)
4374+ .putLong(PREF_KEY_DOWNLOAD_COMPLETED, 0)
4375+ .remove(PREF_KEY_DOWNLOAD_TYPE)
4376+ .commit();
4377 }
4378
4379- private void broadcastInstallerState() {
4380- Intent i = new Intent(SERVICE_STATE);
4381- i.putExtra(SERVICE_STATE, mInstallerState.ordinal());
4382- sendBroadcast(i);
4383- }
4384-
4385 private void updateInstallerState(InstallerState newState) {
4386- mInstallerState = newState;
4387- Intent i = new Intent(SERVICE_STATE);
4388- i.putExtra(SERVICE_STATE, mInstallerState.ordinal());
4389+ mServiceState = newState;
4390+ Intent i = new Intent(SERVICE_STATE_CHANGED);
4391 sendBroadcast(i);
4392 }
4393
4394 /**
4395- * Check whether storage free space is enough.
4396+ * Check whether storage free space is enough for install or update
4397 * @param downloadSize: download size from json. 0 means file already downloaded.
4398- * @return true if stoarge size is ok to go.
4399+ * @return true if storage size is ok to go.
4400 */
4401 private boolean isStorageSpaceEnoughBFDownload(long downloadSize) {
4402- long dataSizeRequired = INSTALL_SIZE_REQUIRED;
4403-
4404- if (workPathInCache) {
4405- if (downloadSize > 0) {
4406- long cacheFreeSpace = Utils.getFreeSpaceInBytes("/cache");
4407- if (cacheFreeSpace < EXTRA_SIZE_REQUIRED + downloadSize) {
4408- return false;
4409- }
4410- }
4411- } else {
4412- dataSizeRequired += downloadSize;
4413+ long dataSizeRequired = downloadSize;
4414+ // if Ubuntu is already installed, we don't need that extra space, it will be freed
4415+ if (!mSharedPreferences.getBoolean(PREF_KEY_UBUNTU_INSTALLED, false)) {
4416+ dataSizeRequired += INSTALL_SIZE_REQUIRED;
4417 }
4418
4419 long dataFreeSpace = Utils.getFreeSpaceInBytes("/data");
4420@@ -1121,114 +1457,127 @@
4421 return true;
4422 }
4423
4424- private static VersionInfo getVersionWithPrefKey(Context c, String prefKey) {
4425- SharedPreferences pref = c.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
4426-
4427- if (VersionInfo.hasValidVersion(pref, prefKey)) {
4428- return new VersionInfo(pref, prefKey);
4429- }
4430- return null;
4431- }
4432-
4433- /**
4434- * Internal helper function to get current DOWNLOAD_VERSION even download is partial
4435- * @param context
4436- * @return version info for download image.
4437- */
4438- private static VersionInfo getDownloadVersion(Context context) {
4439- return getVersionWithPrefKey(context, PREF_KEY_DOWNLOADED_VERSION);
4440- }
4441-
4442- /**
4443- * To get current DOWNLOAD_VERSION for completed download.
4444- * @param context
4445- * @return Version info for download-ed image.
4446- */
4447- public static VersionInfo getDownloadedVersion(Context context) {
4448- VersionInfo v = getVersionWithPrefKey(context, PREF_KEY_DOWNLOADED_VERSION);
4449- if (v != null) {
4450- if (v.mDownloadedSize == 0) return v;
4451- }
4452- return null;
4453- }
4454-
4455- public static VersionInfo getInstalledVersion(Context c) {
4456- return getVersionWithPrefKey(c, PREF_KEY_INSTALLED_VERSION);
4457- }
4458-
4459- public static boolean isUbuntuInstalled(Context c) {
4460- SharedPreferences pref = c.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
4461- if (VersionInfo.hasValidVersion(pref, PREF_KEY_INSTALLED_VERSION)) {
4462- // go to launch screen
4463- return true;
4464- }
4465- return false;
4466- }
4467-
4468- /**
4469- * check if update_command available for upgrade.
4470- *
4471- * @param c
4472- * @return
4473- */
4474- public static boolean isUpgradeable(Context c) {
4475- String cmd = c.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE).getString(PREF_KEY_UPDATE_COMMAND, "");
4476- return (cmd.startsWith("/cache/"));
4477- }
4478-
4479- /**
4480- * check if recovery command is exist on the system.
4481+
4482+ /**
4483+ * Check if recovery command exists on the system.
4484 * system-image in Ubuntu Touch downloaded new version of image at /cache/recovery.
4485 * This function check if there is a ubuntu_command file in /cache/recovery.
4486 *
4487 * The ubuntu_command will be renamed or removed after installation.
4488- *
4489- * @return if there is upgradeable images stored in /cache.
4490- */
4491- public boolean findInstallCommand () {
4492- String[] candidates = {
4493- "/cache/recovery/ubuntu_command",
4494- "/cache/ubunturecovery/ubuntu_command",
4495- };
4496- boolean ret = false;
4497- for(String command: candidates) {
4498- if(new File("/cache").canRead()) {
4499- // if we have permission, we can read /cache.
4500- File cmd = new File(command);
4501- if(cmd.exists() && cmd.isFile()) {
4502- Log.d(TAG, "Found upgrade command - " + cmd.getAbsoluteFile().toString());
4503- // find the upgradeable file, stored into pref.
4504- setUpdateCommand(cmd.getAbsolutePath());
4505- ret = true;
4506- }
4507- } else {
4508- // check the file with su
4509- File workingFolder = new File(mRootOfWorkPath + "/" + TEMP_FOLDER);
4510- if (!workingFolder.exists() && !workingFolder.mkdir()) {
4511- Log.e(TAG, "can not create working folder");
4512- ret = false;
4513- }
4514- try {
4515- int r = executeSUCommands(new String[] {
4516- String.format("%s %s\n", UPGRADECHECKER, command),
4517- });
4518- if(r == 1) {
4519- Log.d(TAG, "Found upgradeable file - " + command);
4520- setUpdateCommand(command);
4521- ret = true;
4522- }
4523- } catch (EShellExecException e) {
4524- ret = false;
4525- }
4526- }
4527- }
4528- if(ret) {
4529- // FIXME we don't know what's the version in /cache.
4530- SharedPreferences pref = getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
4531- VersionInfo v = new VersionInfo(pref, PREF_KEY_INSTALLED_VERSION);
4532- v.storeVersion(pref.edit(), PREF_KEY_DOWNLOADED_VERSION);
4533- }
4534- return ret;
4535+ */
4536+ public void findPendingUpdates () {
4537+
4538+ // check is there is update in linked upgrader folder
4539+ File linkedUpgrader = new File(getFilesDir(), LINKED_UPGRADER_COMMAND );
4540+ File downloadedUpdate = new File(mReleaseFolder, UPDATE_COMMAND );
4541+ boolean updateAvailable = false;
4542+ if (linkedUpgrader.exists()) {
4543+ updateAvailable = true;
4544+ setUpdateCommand(linkedUpgrader.getAbsolutePath());
4545+ // parse file, if it has "format system", it will full install
4546+ if (Utils.parseAndUpdateInstallCommand(this,linkedUpgrader)) {
4547+ // remove channel and server so it's parsed from new install
4548+ mSharedPreferences.edit()
4549+ .remove(PREF_KEY_DOWNLOAD_CHANNEL)
4550+ .remove(PREF_KEY_DOWNLOAD_SERVER).commit();
4551+ }
4552+ // if there is anything downloaded by this installer, we prefer updates from Ubuntu updater
4553+ if (mReleaseFolder.exists()) {
4554+ deleteDirectory(mReleaseFolder);
4555+ }
4556+ } else if (downloadedUpdate.exists()) {
4557+ updateAvailable = true;
4558+ setUpdateCommand(downloadedUpdate.getAbsolutePath());
4559+ }
4560+ if (updateAvailable) {
4561+ mSharedPreferences.edit().putBoolean(PREF_KEY_PENDING_UPDATE,true);
4562+ sendBroadcast(new Intent(UBUNTU_UPDATE_PENDING));
4563+ showUbuntuUpdateNotification(R.string.notification_update_available);
4564+ }
4565+ // check if there is also Android update
4566+ checkForPendingAndroidUpdate();
4567+ File androidUpdate = new File(getFilesDir(), PENDING_ANDROID_UPDATE);
4568+ if (androidUpdate.exists()) {
4569+ // delete file, we don't need it anymore
4570+ androidUpdate.delete();
4571+ sendBroadcast(new Intent(ANDROID_UPDATE_PENDING));
4572+ // show notification only if it is desired by user
4573+ if (!mSharedPreferences.getBoolean(PREF_KEY_ANDROID_UPDATE_DISMISSED, false)) {
4574+ showAndroidUpdateNotification();
4575+ }
4576+ }
4577+ }
4578+
4579+ /**
4580+ * Check if there is pending android update zip package already downloaded
4581+ */
4582+ private void checkForPendingAndroidUpdate() {
4583+ try {
4584+ executeSUCommands(new String[] {
4585+ String.format("if [[ ! -z $(busybox find /cache/ -maxdepth 1 -iname '*%s*%s*.zip') ]]; then touch %s/%s; chmod 777 %s/%s; fi\n",
4586+ Build.PRODUCT.toLowerCase(Locale.US),
4587+ Build.ID,
4588+ getFilesDir().toString(),
4589+ PENDING_ANDROID_UPDATE,
4590+ getFilesDir().toString(),
4591+ PENDING_ANDROID_UPDATE)
4592+ });
4593+ } catch (EShellExecException e1) {
4594+ // ignore error
4595+ }
4596+ }
4597+
4598+ /**
4599+ * Check if there is available update to be downloaded
4600+ */
4601+ private void checkForAvailableUpdate() {
4602+ String channel = mSharedPreferences.getString(PREF_KEY_DOWNLOAD_CHANNEL, null);
4603+ String server = mSharedPreferences.getString(PREF_KEY_DOWNLOAD_SERVER, null);
4604+ if (channel == null || server == null) {
4605+ // no stored channel or server URL
4606+ return;
4607+ }
4608+ String jsonStr = Utils.httpDownload(String.format("%s/%s", server, channel));
4609+ if (jsonStr != null) {
4610+ List<Image> releases = JsonChannelParser.getAvailableReleases(jsonStr, ReleaseType.DELTA);
4611+ if (releases.size() == 0 || releases.get(0).files.length == 0 ) {
4612+ // something is wrong, empty release
4613+ Log.e(TAG, "Empty releas");
4614+ return;
4615+ }
4616+ // look for version with right base
4617+ String baseStr = mSharedPreferences.getString(PREF_KEY_VERSION_NUMBER, "0");
4618+ int base = Integer.valueOf(baseStr);
4619+ for(Image r : releases) {
4620+ if (r.base == base) {
4621+ Log.d(TAG, "Found update to version:" + r.version);
4622+ mSharedPreferences.edit().putBoolean(PREF_KEY_PENDING_UPDATE, true).commit();
4623+ Intent intent = new Intent(UBUNTU_UPDATE_AVAILABLE);
4624+ sendBroadcast(intent);
4625+ // show notification
4626+ showUbuntuUpdateNotification(R.string.notification_update_available);
4627+ return;
4628+ }
4629+ }
4630+ }
4631+ }
4632+
4633+ /**
4634+ * Update used storage
4635+ * @return broadcast about data being updated
4636+ */
4637+ private void doUpdateStorageUse() {
4638+ try {
4639+ executeSUCommands(
4640+ new String[]{
4641+ String.format("busybox du -sm %s || true\n", UBUNTU_ROOTFS),
4642+ String.format("busybox du -sm %s || true\n", UBUNTU_SYSTEM_DATA),
4643+ String.format("busybox du -sm %s || true\n", UBUNTU_USER_DATA),
4644+ } );
4645+ } catch (EShellExecException e) {
4646+ // ignore error
4647+ e.printStackTrace();
4648+ }
4649 }
4650
4651 /**
4652@@ -1236,7 +1585,7 @@
4653 * @file absolute file path of Ubuntu Command file.
4654 */
4655 private String getUpdateCommand(){
4656- return getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE).getString(PREF_KEY_UPDATE_COMMAND, "");
4657+ return mSharedPreferences.getString(PREF_KEY_UPDATE_COMMAND, "");
4658 }
4659
4660 /**
4661@@ -1244,27 +1593,60 @@
4662 * @file absolute file path of Ubuntu Command file.
4663 */
4664 private void setUpdateCommand(String file){
4665- getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE).
4666- edit().
4667- putString(PREF_KEY_UPDATE_COMMAND, file).
4668- commit();
4669+ mSharedPreferences.edit().putString(PREF_KEY_UPDATE_COMMAND, file).commit();
4670 }
4671
4672 /**
4673 * clean update command string stored in shared preferences.
4674 */
4675 private void cleanUpdateCommand() {
4676- this.setUpdateCommand("");
4677- }
4678-
4679- private void broadcastProgress(int val, String progress) {
4680- Intent i = new Intent(PROGRESS);
4681- i.putExtra(PROGRESS_EXTRA_INT, val);
4682- if (progress!= null) {
4683- i.putExtra(PROGRESS_EXTRA_TEXT, progress);
4684+ mSharedPreferences.edit()
4685+ .remove(PREF_KEY_UPDATE_COMMAND)
4686+ .commit();
4687+ }
4688+
4689+ /**
4690+ * Clean installed version info and all othe values
4691+ */
4692+ private void setUbuntuUninstalled() {
4693+ mSharedPreferences.edit()
4694+ .remove(PREF_KEY_SIZE_ROOTFS)
4695+ .remove(PREF_KEY_VERSION_NUMBER)
4696+ .remove(PREF_KEY_VERSION_CHANNEL)
4697+ .remove(PREF_KEY_VERSION_CHANNEL_TARGET)
4698+ .remove(PREF_KEY_VERSION_DESCRIPTION)
4699+ .remove(PREF_KEY_VERSION_BASE)
4700+ .putBoolean(PREF_KEY_UBUNTU_INSTALLED, false)
4701+ .putBoolean(PREF_KEY_PENDING_UPDATE, false)
4702+ .commit();
4703+ Utils.unregisterUpdateCheckAlarm(this);
4704+ dismissUbuntuNotification();
4705+ dismissProgressNotification();
4706+ dismissUbuntuUpdateNotification();
4707+ }
4708+
4709+ private void broadcastProgress(String progress) {
4710+
4711+ Intent i = null;
4712+ if (progress == null) {
4713+ i = new Intent(PROGRESS);
4714+ // update notification
4715+ showProgressNotification();
4716+ } else {
4717+ mActionOutput += "\n" + progress;
4718+ i = new Intent(PROGRESS_OUPUT);
4719 }
4720 sendBroadcast(i);
4721 }
4722+
4723+ private boolean isNetworkAvailable() {
4724+ final ConnectivityManager conMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
4725+ final NetworkInfo activeNetwork = conMgr.getActiveNetworkInfo();
4726+ if (activeNetwork != null && activeNetwork.isConnected()) {
4727+ return true;
4728+ }
4729+ return false;
4730+ }
4731
4732 /**
4733 * Check if there is downloaded release ready to install.
4734@@ -1272,25 +1654,157 @@
4735 * @param context
4736 * @return true if there is downloaded release ready to install
4737 */
4738- public static boolean checkifReadyToInstall(Context context) {
4739- SharedPreferences pref = context.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
4740- VersionInfo versionInfo = getDownloadVersion(context);
4741- if (versionInfo == null) return false;
4742- if (versionInfo.getDownloadedSize() != 0) return false;
4743-
4744- String command = pref.getString(PREF_KEY_UPDATE_COMMAND, "");
4745+ public static boolean isReadyToInstall(SharedPreferences sp) {
4746+ String command = sp.getString(PREF_KEY_UPDATE_COMMAND, "");
4747 Log.d(TAG, "checkifReadyToInstall");
4748- if (!command.equals("")) {
4749- if (new File(command).exists() || command.startsWith("/cache")) {
4750- Log.d(TAG, "checkifReadyToInstall - found command file " + command);
4751- return true;
4752+ if (!command.equals("") && new File(command).exists() ) {
4753+ Log.d(TAG, "checkifReadyToInstall - found command file " + command);
4754+ return true;
4755+ } else {
4756+ sp.edit().remove(PREF_KEY_UPDATE_COMMAND).commit();
4757+ return false;
4758+ }
4759+ }
4760+
4761+ public static boolean isDownloadActive(SharedPreferences sp) {
4762+ return sp.getBoolean(UbuntuInstallService.PREF_KEY_DOWNLOAD_ACTIVE, false);
4763+ }
4764+
4765+ public static void startInstallationIfPossible(Context c) {
4766+ // continue only if battery charge if over 30%
4767+ if ( MIN_INSTALL_BATTERY_CHARGE > getBatteryCharge(c) ) {
4768+ Utils.showToast(c, R.string.not_enough_charge_warning);
4769+ } else {
4770+ c.startService(new Intent(UbuntuInstallService.INSTALL_UBUNTU));
4771+ }
4772+ }
4773+
4774+
4775+ public static int getBatteryCharge(Context c) {
4776+ IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
4777+ Intent batteryStatus = c.registerReceiver(null, ifilter);
4778+ int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
4779+ int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
4780+ return (level * 100 / scale);
4781+ }
4782+
4783+ private void showUbuntuNotification(int textRsid, Class<?> act) {
4784+ Log.d(TAG, "showUbuntuNotification:" + getString(textRsid));
4785+ dismissProgressNotification();
4786+ mUbuntuNotification = showNotification(textRsid,
4787+ R.id.ubuntu_notification, act);
4788+ }
4789+
4790+ private void showUbuntuUpdateNotification(int textRsid) {
4791+ mUbuntuUpdateNotification = showNotification(textRsid,
4792+ R.id.ubuntu_update_notification,
4793+ LaunchActivity.class);
4794+ }
4795+
4796+ private void showAndroidUpdateNotification() {
4797+ mAndroidUpdateNotification = showNotification(
4798+ R.string.notification_android_update,
4799+ R.id.android_update_notification,
4800+ LaunchActivity.class);
4801+ }
4802+
4803+
4804+ private Notification showNotification(int textrRsid, int notificationId, Class<?> act) {
4805+ Intent updateActivity = new Intent(this, act);
4806+ updateActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
4807+ PendingIntent pendingUpdate = PendingIntent.getActivity(
4808+ this, 0, updateActivity, PendingIntent.FLAG_UPDATE_CURRENT);
4809+
4810+ Notification notification = new Notification.Builder(this)
4811+ .setAutoCancel(true)
4812+ .setSmallIcon(R.drawable.ic_stat_notify_ubuntu)
4813+ .setContentTitle(getString(R.string.notification_title))
4814+ .setContentText(getString(textrRsid))
4815+ .setTicker(getString(textrRsid))
4816+ .setContentIntent(pendingUpdate)
4817+ .build();
4818+
4819+ mNotificationManager.notify(notificationId, notification);
4820+ return notification;
4821+ }
4822+
4823+ private void showProgressNotification() {
4824+ // we are showing progress, no need for update notification
4825+ dismissUbuntuUpdateNotification();
4826+ // choose text based on state downloading/installing and type FULL/DELTA
4827+ ReleaseType releaseType = ReleaseType.fromValue( // use delta as default for unknown updates
4828+ mSharedPreferences.getInt(PREF_KEY_DOWNLOAD_TYPE, ReleaseType.DELTA.getValue()));
4829+ int textRsid = 0;
4830+ if (mServiceState == InstallerState.DOWNLOADING) {
4831+ if (releaseType == ReleaseType.FULL) {
4832+ textRsid = R.string.notification_downloading_release;
4833 } else {
4834- pref.edit().putString(PREF_KEY_UPDATE_COMMAND, "").commit();
4835- VersionInfo.storeEmptyVersion(pref.edit(), PREF_KEY_DOWNLOADED_VERSION);
4836- return false;
4837+ textRsid = R.string.notification_downloading_update;
4838 }
4839+ } else if (mServiceState == InstallerState.INSTALLING) {
4840+ if (releaseType == ReleaseType.FULL) {
4841+ textRsid = R.string.notification_installing_release;
4842+ } else {
4843+ textRsid = R.string.notification_installing_update;
4844+ }
4845+ } else {
4846+ // don't show any notification
4847+ return;
4848 }
4849- VersionInfo.storeEmptyVersion(pref.edit(), PREF_KEY_DOWNLOADED_VERSION);
4850- return false;
4851+ Intent updateActivity = new Intent(this, InstallActivity.class);
4852+ updateActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
4853+ PendingIntent pendingUpdate = PendingIntent.getActivity(
4854+ this, 0, updateActivity, PendingIntent.FLAG_UPDATE_CURRENT);
4855+
4856+ mUbuntuProgressNotification = new Notification.Builder(this)
4857+ .setAutoCancel(true)
4858+ .setSmallIcon(R.drawable.ic_stat_notify_ubuntu)
4859+ .setContentTitle(getString(R.string.notification_title))
4860+ .setContentText(getString(textRsid))
4861+ .setTicker(getString(textRsid))
4862+ .setContentIntent(pendingUpdate)
4863+ .setProgress(100, mLastSignalledProgress, false)
4864+ .build();
4865+ mNotificationManager.notify(R.id.ubuntu_progress_notification, mUbuntuProgressNotification);
4866 }
4867+
4868+ /**
4869+ * Dismiss progress notification
4870+ */
4871+ public void dismissProgressNotification() {
4872+ if (mUbuntuProgressNotification != null) {
4873+ mNotificationManager.cancel(R.id.ubuntu_progress_notification);
4874+ mUbuntuProgressNotification = null;
4875+ }
4876+ }
4877+
4878+ /**
4879+ * Dismiss Ubuntu notification
4880+ */
4881+ public void dismissUbuntuNotification() {
4882+ if (mUbuntuNotification != null) {
4883+ mUbuntuNotification = null;
4884+ mNotificationManager.cancel(R.id.ubuntu_notification);
4885+ }
4886+ }
4887+
4888+ /**
4889+ * Dismiss Ubuntu notification
4890+ */
4891+ public void dismissUbuntuUpdateNotification() {
4892+ if (mUbuntuUpdateNotification != null) {
4893+ mUbuntuUpdateNotification = null;
4894+ mNotificationManager.cancel(R.id.ubuntu_update_notification);
4895+ }
4896+ }
4897+
4898+ /**
4899+ * Dismiss Android notification
4900+ */
4901+ public void dismissAndroidNotification() {
4902+ if (mAndroidUpdateNotification != null) {
4903+ mAndroidUpdateNotification = null;
4904+ mNotificationManager.cancel(R.id.android_update_notification);
4905+ }
4906+ }
4907 }
4908
4909=== modified file 'src/com/canonical/ubuntu/installer/Utils.java'
4910--- src/com/canonical/ubuntu/installer/Utils.java 2014-01-09 08:54:46 +0000
4911+++ src/com/canonical/ubuntu/installer/Utils.java 2014-06-09 11:59:50 +0000
4912@@ -1,14 +1,36 @@
4913+/*
4914+ * This file is part of Ubuntu dualboot installer for Android.
4915+ * Copyright 2013 Canonical Ltd.
4916+ *
4917+ * Ubuntu dualboot installer is free software: you can redistribute it and/or modify
4918+ * it under the terms of the GNU General Public License as published by
4919+ * the Free Software Foundation.
4920+ *
4921+ * Ubuntu dualboot installer is distributed in the hope that it will be useful,
4922+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4923+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
4924+ * See the GNU General Public License for more details.
4925+ *
4926+ * You should have received a copy of the GNU General Public License
4927+ * along with Ubuntu dualboot installer. If not, see <http://www.gnu.org/licenses/>.
4928+ */
4929+
4930 package com.canonical.ubuntu.installer;
4931
4932+import android.app.AlarmManager;
4933 import android.app.AlertDialog;
4934+import android.app.PendingIntent;
4935 import android.content.Context;
4936 import android.content.DialogInterface;
4937 import android.content.Intent;
4938+import android.content.SharedPreferences;
4939 import android.content.res.AssetManager;
4940 import android.net.wifi.WifiManager;
4941 import android.os.Build;
4942 import android.os.StatFs;
4943 import android.util.Log;
4944+import android.view.Gravity;
4945+import android.widget.TextView;
4946 import android.widget.Toast;
4947
4948 import org.apache.http.HttpEntity;
4949@@ -22,6 +44,7 @@
4950 import java.io.File;
4951 import java.io.FileInputStream;
4952 import java.io.FileOutputStream;
4953+import java.io.FileReader;
4954 import java.io.IOException;
4955 import java.io.InputStream;
4956 import java.io.InputStreamReader;
4957@@ -30,6 +53,7 @@
4958 import java.io.StringWriter;
4959 import java.io.Writer;
4960 import java.security.MessageDigest;
4961+import java.util.ArrayList;
4962 import java.util.List;
4963 import java.util.Locale;
4964
4965@@ -191,18 +215,43 @@
4966 }
4967
4968 public static String getDeviceModel() {
4969- return Build.DEVICE.toLowerCase(Locale.US);
4970+ String deviceModel = Build.DEVICE.toLowerCase(Locale.US);
4971+ if (deviceModel.equals("deb") || deviceModel.equals("tilapia")) {
4972+ deviceModel = "flo";
4973+ }
4974+ if (isBringupMode()) {
4975+ deviceModel = "mako";
4976+ }
4977+ return deviceModel;
4978 }
4979
4980 public static boolean isDeviceSupported() {
4981 String path = getBootPartitionPath();
4982- if ("".equals(path)) return false;
4983+ if ("".equals(path)) {
4984+ return false;
4985+ } else {
4986+ return true;
4987+ }
4988+ }
4989+
4990+ /**
4991+ * Check if device is in the list of know test devices
4992+ * of if we are in bringup mode
4993+ */
4994+ public static boolean isBringupMode() {
4995+ if (!UbuntuInstallService.BRINGUP_MODE) {
4996+ String deviceModel = Build.DEVICE.toLowerCase(Locale.US);
4997+ if ("bq_aquaris5".equals(deviceModel)) {
4998+ return true;
4999+ }
5000+ return false;
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches