Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
def7634b
Unverified
Commit
def7634b
authored
Jun 25, 2018
by
Sarah Zakarias
Committed by
GitHub
Jun 25, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support running apk with more than one activity (#18716)
parent
87a06770
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
258 additions
and
52 deletions
+258
-52
application_package.dart
packages/flutter_tools/lib/src/application_package.dart
+129
-19
application_package_test.dart
packages/flutter_tools/test/application_package_test.dart
+129
-33
No files found.
packages/flutter_tools/lib/src/application_package.dart
View file @
def7634b
...
...
@@ -60,8 +60,16 @@ class AndroidApk extends ApplicationPackage {
return
null
;
}
final
List
<
String
>
aaptArgs
=
<
String
>[
aaptPath
,
'dump'
,
'badging'
,
applicationBinary
];
final
ApkManifestData
data
=
ApkManifestData
.
parseFromAaptBadging
(
runCheckedSync
(
aaptArgs
));
final
List
<
String
>
aaptArgs
=
<
String
>[
aaptPath
,
'dump'
,
'xmltree'
,
applicationBinary
,
'AndroidManifest.xml'
,
];
final
ApkManifestData
data
=
ApkManifestData
.
parseFromXmlDump
(
runCheckedSync
(
aaptArgs
));
if
(
data
==
null
)
{
printError
(
'Unable to read manifest info from
$applicationBinary
.'
);
...
...
@@ -116,9 +124,12 @@ class AndroidApk extends ApplicationPackage {
for
(
xml
.
XmlElement
category
in
document
.
findAllElements
(
'category'
))
{
if
(
category
.
getAttribute
(
'android:name'
)
==
'android.intent.category.LAUNCHER'
)
{
final
xml
.
XmlElement
activity
=
category
.
parent
.
parent
;
final
String
activityName
=
activity
.
getAttribute
(
'android:name'
);
launchActivity
=
'
$packageId
/
$activityName
'
;
break
;
final
String
enabled
=
activity
.
getAttribute
(
'android:enabled'
);
if
(
enabled
==
null
||
enabled
==
'true'
)
{
final
String
activityName
=
activity
.
getAttribute
(
'android:name'
);
launchActivity
=
'
$packageId
/
$activityName
'
;
break
;
}
}
}
...
...
@@ -349,34 +360,133 @@ class ApplicationPackageStore {
}
}
class
_Entry
{
_Element
parent
;
int
level
;
}
class
_Element
extends
_Entry
{
List
<
_Entry
>
children
;
String
name
;
_Element
.
fromLine
(
String
line
,
_Element
parent
)
{
// E: application (line=29)
final
List
<
String
>
parts
=
line
.
trimLeft
().
split
(
' '
);
name
=
parts
[
1
];
level
=
line
.
length
-
line
.
trimLeft
().
length
;
this
.
parent
=
parent
;
children
=
<
_Entry
>[];
}
void
addChild
(
_Entry
child
)
{
children
.
add
(
child
);
}
_Attribute
firstAttribute
(
String
name
)
{
return
children
.
firstWhere
(
(
_Entry
e
)
=>
e
is
_Attribute
&&
e
.
key
.
startsWith
(
name
),
orElse:
()
=>
null
,
);
}
_Element
firstElement
(
String
name
)
{
return
children
.
firstWhere
(
(
_Entry
e
)
=>
e
is
_Element
&&
e
.
name
.
startsWith
(
name
),
orElse:
()
=>
null
,
);
}
Iterable
<
_Entry
>
allElements
(
String
name
)
{
return
children
.
where
(
(
_Entry
e
)
=>
e
is
_Element
&&
e
.
name
.
startsWith
(
name
));
}
}
class
_Attribute
extends
_Entry
{
String
key
;
String
value
;
_Attribute
.
fromLine
(
String
line
,
_Element
parent
)
{
// A: android:label(0x01010001)="hello_world" (Raw: "hello_world")
const
String
attributePrefix
=
'A: '
;
final
List
<
String
>
keyVal
=
line
.
substring
(
line
.
indexOf
(
attributePrefix
)
+
attributePrefix
.
length
)
.
split
(
'='
);
key
=
keyVal
[
0
];
value
=
keyVal
[
1
];
level
=
line
.
length
-
line
.
trimLeft
().
length
;
this
.
parent
=
parent
;
}
}
class
ApkManifestData
{
ApkManifestData
.
_
(
this
.
_data
);
static
ApkManifestData
parseFrom
AaptBadging
(
String
data
)
{
static
ApkManifestData
parseFrom
XmlDump
(
String
data
)
{
if
(
data
==
null
||
data
.
trim
().
isEmpty
)
return
null
;
// package: name='io.flutter.gallery' versionCode='1' versionName='0.0.1' platformBuildVersionName='NMR1'
// launchable-activity: name='io.flutter.app.FlutterActivity' label='' icon=''
final
Map
<
String
,
Map
<
String
,
String
>>
map
=
<
String
,
Map
<
String
,
String
>>{};
final
List
<
String
>
lines
=
data
.
split
(
'
\n
'
);
assert
(
lines
.
length
>
3
);
final
RegExp
keyValueRegex
=
new
RegExp
(
r"(\S+?)='(.*?)'"
);
final
_Element
manifest
=
new
_Element
.
fromLine
(
lines
[
1
],
null
);
_Element
currentElement
=
manifest
;
for
(
String
line
in
data
.
split
(
'
\n
'
))
{
final
int
index
=
line
.
indexOf
(
':'
);
if
(
index
!=
-
1
)
{
final
String
name
=
line
.
substring
(
0
,
index
);
line
=
line
.
substring
(
index
+
1
).
trim
();
for
(
String
line
in
lines
.
skip
(
2
))
{
final
String
trimLine
=
line
.
trimLeft
();
final
int
level
=
line
.
length
-
trimLine
.
length
;
final
Map
<
String
,
String
>
entries
=
<
String
,
String
>{};
map
[
name
]
=
entries
;
// Handle level out
while
(
level
<=
currentElement
.
level
)
{
currentElement
=
currentElement
.
parent
;
}
for
(
Match
m
in
keyValueRegex
.
allMatches
(
line
))
{
entries
[
m
.
group
(
1
)]
=
m
.
group
(
2
);
if
(
level
>
currentElement
.
level
)
{
switch
(
trimLine
[
0
])
{
case
'A'
:
currentElement
.
addChild
(
new
_Attribute
.
fromLine
(
line
,
currentElement
));
break
;
case
'E'
:
final
_Element
element
=
new
_Element
.
fromLine
(
line
,
currentElement
);
currentElement
.
addChild
(
element
);
currentElement
=
element
;
}
}
}
final
_Element
application
=
manifest
.
firstElement
(
'application'
);
assert
(
application
!=
null
);
final
Iterable
<
_Entry
>
activities
=
application
.
allElements
(
'activity'
);
_Element
launchActivity
;
for
(
_Element
activity
in
activities
)
{
final
_Attribute
enabled
=
activity
.
firstAttribute
(
'android:enabled'
);
if
(
enabled
==
null
||
enabled
.
value
.
contains
(
'0xffffffff'
))
{
launchActivity
=
activity
;
break
;
}
}
final
_Attribute
package
=
manifest
.
firstAttribute
(
'package'
);
// "io.flutter.examples.hello_world" (Raw: "io.flutter.examples.hello_world")
final
String
packageName
=
package
.
value
.
substring
(
1
,
package
.
value
.
indexOf
(
'" '
));
if
(
launchActivity
==
null
)
{
printError
(
'Error running
$packageName
. Default activity not found'
);
return
null
;
}
final
_Attribute
nameAttribute
=
launchActivity
.
firstAttribute
(
'android:name'
);
// "io.flutter.examples.hello_world.MainActivity" (Raw: "io.flutter.examples.hello_world.MainActivity")
final
String
activityName
=
nameAttribute
.
value
.
substring
(
1
,
nameAttribute
.
value
.
indexOf
(
'" '
));
final
Map
<
String
,
Map
<
String
,
String
>>
map
=
<
String
,
Map
<
String
,
String
>>{};
map
[
'package'
]
=
<
String
,
String
>{
'name'
:
packageName
};
map
[
'launchable-activity'
]
=
<
String
,
String
>{
'name'
:
activityName
};
return
new
ApkManifestData
.
_
(
map
);
}
...
...
packages/flutter_tools/test/application_package_test.dart
View file @
def7634b
...
...
@@ -18,13 +18,24 @@ import 'src/context.dart';
void
main
(
)
{
group
(
'ApkManifestData'
,
()
{
testUsingContext
(
'parse sdk'
,
()
{
final
ApkManifestData
data
=
ApkManifestData
.
parseFromAaptBadging
(
_aaptData
);
test
(
'Select explicity enabled activity'
,
()
{
final
ApkManifestData
data
=
ApkManifestData
.
parseFromXmlDump
(
_aaptDataWithExplicitEnabledActivity
);
expect
(
data
,
isNotNull
);
expect
(
data
.
packageName
,
'io.flutter.gallery'
);
expect
(
data
.
launchableActivityName
,
'io.flutter.app.FlutterActivity'
);
expect
(
data
.
data
[
'application'
][
'label'
],
'Flutter Gallery'
);
expect
(
data
.
packageName
,
'io.flutter.examples.hello_world'
);
expect
(
data
.
launchableActivityName
,
'io.flutter.examples.hello_world.MainActivity2'
);
});
test
(
'Select default enabled activity'
,
()
{
final
ApkManifestData
data
=
ApkManifestData
.
parseFromXmlDump
(
_aaptDataWithDefaultEnabledActivity
);
expect
(
data
,
isNotNull
);
expect
(
data
.
packageName
,
'io.flutter.examples.hello_world'
);
expect
(
data
.
launchableActivityName
,
'io.flutter.examples.hello_world.MainActivity2'
);
});
testUsingContext
(
'Error on no enabled activity'
,
()
{
final
ApkManifestData
data
=
ApkManifestData
.
parseFromXmlDump
(
_aaptDataWithNoEnabledActivity
);
expect
(
data
,
isNull
);
final
BufferLogger
logger
=
context
[
Logger
];
expect
(
logger
.
errorText
,
'Error running io.flutter.examples.hello_world. Default activity not found
\n
'
);
});
});
...
...
@@ -152,33 +163,6 @@ void main() {
});
}
const
String
_aaptData
=
'''
package: name='
io
.
flutter
.
gallery
' versionCode='
1
' versionName='
0.0
.
1
' platformBuildVersionName='
NMR1
'
sdkVersion:'
14
'
targetSdkVersion:'
21
'
uses-permission: name='
android
.
permission
.
INTERNET
'
application-label:'
Flutter
Gallery
'
application-icon-160:'
res
/
mipmap
-
mdpi
-
v4
/
ic_launcher
.
png
'
application-icon-240:'
res
/
mipmap
-
hdpi
-
v4
/
ic_launcher
.
png
'
application-icon-320:'
res
/
mipmap
-
xhdpi
-
v4
/
ic_launcher
.
png
'
application-icon-480:'
res
/
mipmap
-
xxhdpi
-
v4
/
ic_launcher
.
png
'
application-icon-640:'
res
/
mipmap
-
xxxhdpi
-
v4
/
ic_launcher
.
png
'
application: label='
Flutter
Gallery
' icon='
res
/
mipmap
-
mdpi
-
v4
/
ic_launcher
.
png
'
application-debuggable
launchable-activity: name='
io
.
flutter
.
app
.
FlutterActivity
' label='' icon=''
feature-group: label=''
uses-feature: name='
android
.
hardware
.
screen
.
portrait
'
uses-implied-feature: name='
android
.
hardware
.
screen
.
portrait
' reason='
one
or
more
activities
have
specified
a
portrait
orientation
'
uses-feature: name='
android
.
hardware
.
touchscreen
'
uses-implied-feature: name='
android
.
hardware
.
touchscreen
' reason='
default
feature
for
all
apps
'
main
supports-screens: '
small
' '
normal
' '
large
' '
xlarge
'
supports-any-density: '
true
'
locales: '
--
_
--
'
densities: '
160
' '
240
' '
320
' '
480
' '
640
'
native-code: '
armeabi
-
v7a
'
'''
;
final
Map
<
String
,
String
>
_swiftBuildSettings
=
<
String
,
String
>{
'ARCHS'
:
'arm64'
,
'ASSETCATALOG_COMPILER_APPICON_NAME'
:
'AppIcon'
,
...
...
@@ -192,6 +176,118 @@ final Map<String, String> _swiftBuildSettings = <String, String>{
'SWIFT_VERSION'
:
'3.0'
,
};
const
String
_aaptDataWithExplicitEnabledActivity
=
'''N: android=http://schemas.android.com/apk/res/android
E: manifest (line=7)
A: android:versionCode(0x0101021b)=(type 0x10)0x1
A: android:versionName(0x0101021c)="0.0.1" (Raw: "0.0.1")
A: package="io.flutter.examples.hello_world" (Raw: "io.flutter.examples.hello_world")
E: uses-sdk (line=12)
A: android:minSdkVersion(0x0101020c)=(type 0x10)0x10
A: android:targetSdkVersion(0x01010270)=(type 0x10)0x1b
E: uses-permission (line=21)
A: android:name(0x01010003)="android.permission.INTERNET" (Raw: "android.permission.INTERNET")
E: application (line=29)
A: android:label(0x01010001)="hello_world" (Raw: "hello_world")
A: android:icon(0x01010002)=@0x7f010000
A: android:name(0x01010003)="io.flutter.app.FlutterApplication" (Raw: "io.flutter.app.FlutterApplication")
A: android:debuggable(0x0101000f)=(type 0x12)0xffffffff
E: activity (line=34)
A: android:theme(0x01010000)=@0x1030009
A: android:name(0x01010003)="io.flutter.examples.hello_world.MainActivity" (Raw: "io.flutter.examples.hello_world.MainActivity")
A: android:enabled(0x0101000e)=(type 0x12)0x0
A: android:launchMode(0x0101001d)=(type 0x10)0x1
A: android:configChanges(0x0101001f)=(type 0x11)0x400035b4
A: android:windowSoftInputMode(0x0101022b)=(type 0x11)0x10
A: android:hardwareAccelerated(0x010102d3)=(type 0x12)0xffffffff
E: intent-filter (line=42)
E: action (line=43)
A: android:name(0x01010003)="android.intent.action.MAIN" (Raw: "android.intent.action.MAIN")
E: category (line=45)
A: android:name(0x01010003)="android.intent.category.LAUNCHER" (Raw: "android.intent.category.LAUNCHER")
E: activity (line=48)
A: android:theme(0x01010000)=@0x1030009
A: android:label(0x01010001)="app2" (Raw: "app2")
A: android:name(0x01010003)="io.flutter.examples.hello_world.MainActivity2" (Raw: "io.flutter.examples.hello_world.MainActivity2")
A: android:enabled(0x0101000e)=(type 0x12)0xffffffff
E: intent-filter (line=53)
E: action (line=54)
A: android:name(0x01010003)="android.intent.action.MAIN" (Raw: "android.intent.action.MAIN")
E: category (line=56)
A: android:name(0x01010003)="android.intent.category.LAUNCHER" (Raw: "android.intent.category.LAUNCHER")'''
;
const
String
_aaptDataWithDefaultEnabledActivity
=
'''N: android=http://schemas.android.com/apk/res/android
E: manifest (line=7)
A: android:versionCode(0x0101021b)=(type 0x10)0x1
A: android:versionName(0x0101021c)="0.0.1" (Raw: "0.0.1")
A: package="io.flutter.examples.hello_world" (Raw: "io.flutter.examples.hello_world")
E: uses-sdk (line=12)
A: android:minSdkVersion(0x0101020c)=(type 0x10)0x10
A: android:targetSdkVersion(0x01010270)=(type 0x10)0x1b
E: uses-permission (line=21)
A: android:name(0x01010003)="android.permission.INTERNET" (Raw: "android.permission.INTERNET")
E: application (line=29)
A: android:label(0x01010001)="hello_world" (Raw: "hello_world")
A: android:icon(0x01010002)=@0x7f010000
A: android:name(0x01010003)="io.flutter.app.FlutterApplication" (Raw: "io.flutter.app.FlutterApplication")
A: android:debuggable(0x0101000f)=(type 0x12)0xffffffff
E: activity (line=34)
A: android:theme(0x01010000)=@0x1030009
A: android:name(0x01010003)="io.flutter.examples.hello_world.MainActivity" (Raw: "io.flutter.examples.hello_world.MainActivity")
A: android:enabled(0x0101000e)=(type 0x12)0x0
A: android:launchMode(0x0101001d)=(type 0x10)0x1
A: android:configChanges(0x0101001f)=(type 0x11)0x400035b4
A: android:windowSoftInputMode(0x0101022b)=(type 0x11)0x10
A: android:hardwareAccelerated(0x010102d3)=(type 0x12)0xffffffff
E: intent-filter (line=42)
E: action (line=43)
A: android:name(0x01010003)="android.intent.action.MAIN" (Raw: "android.intent.action.MAIN")
E: category (line=45)
A: android:name(0x01010003)="android.intent.category.LAUNCHER" (Raw: "android.intent.category.LAUNCHER")
E: activity (line=48)
A: android:theme(0x01010000)=@0x1030009
A: android:label(0x01010001)="app2" (Raw: "app2")
A: android:name(0x01010003)="io.flutter.examples.hello_world.MainActivity2" (Raw: "io.flutter.examples.hello_world.MainActivity2")
E: intent-filter (line=53)
E: action (line=54)
A: android:name(0x01010003)="android.intent.action.MAIN" (Raw: "android.intent.action.MAIN")
E: category (line=56)
A: android:name(0x01010003)="android.intent.category.LAUNCHER" (Raw: "android.intent.category.LAUNCHER")'''
;
const
String
_aaptDataWithNoEnabledActivity
=
'''N: android=http://schemas.android.com/apk/res/android
E: manifest (line=7)
A: android:versionCode(0x0101021b)=(type 0x10)0x1
A: android:versionName(0x0101021c)="0.0.1" (Raw: "0.0.1")
A: package="io.flutter.examples.hello_world" (Raw: "io.flutter.examples.hello_world")
E: uses-sdk (line=12)
A: android:minSdkVersion(0x0101020c)=(type 0x10)0x10
A: android:targetSdkVersion(0x01010270)=(type 0x10)0x1b
E: uses-permission (line=21)
A: android:name(0x01010003)="android.permission.INTERNET" (Raw: "android.permission.INTERNET")
E: application (line=29)
A: android:label(0x01010001)="hello_world" (Raw: "hello_world")
A: android:icon(0x01010002)=@0x7f010000
A: android:name(0x01010003)="io.flutter.app.FlutterApplication" (Raw: "io.flutter.app.FlutterApplication")
A: android:debuggable(0x0101000f)=(type 0x12)0xffffffff
E: activity (line=34)
A: android:theme(0x01010000)=@0x1030009
A: android:name(0x01010003)="io.flutter.examples.hello_world.MainActivity" (Raw: "io.flutter.examples.hello_world.MainActivity")
A: android:enabled(0x0101000e)=(type 0x12)0x0
A: android:launchMode(0x0101001d)=(type 0x10)0x1
A: android:configChanges(0x0101001f)=(type 0x11)0x400035b4
A: android:windowSoftInputMode(0x0101022b)=(type 0x11)0x10
A: android:hardwareAccelerated(0x010102d3)=(type 0x12)0xffffffff
E: intent-filter (line=42)
E: action (line=43)
A: android:name(0x01010003)="android.intent.action.MAIN" (Raw: "android.intent.action.MAIN")
E: category (line=45)
A: android:name(0x01010003)="android.intent.category.LAUNCHER" (Raw: "android.intent.category.LAUNCHER")'''
;
class
MockIosWorkFlow
extends
Mock
implements
IOSWorkflow
{
@override
String
getPlistValueFromFile
(
String
path
,
String
key
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment