Skip to content
Permalink
Browse files

Add Feature: silent install.

  • Loading branch information
heruoxin committed Nov 7, 2018
1 parent 098303b commit d79a2d93d3ed4d5b229a17bc1a0b1ab3ef3fce07
Showing with 814 additions and 25 deletions.
  1. +29 −0 .idea/codeStyles/Project.xml
  2. +2 −1 .idea/modules.xml
  3. +1 −1 build.gradle
  4. 0 {app → freeze-app}/.gitignore
  5. +2 −0 {app → freeze-app}/build.gradle
  6. 0 {app → freeze-app}/proguard-rules.pro
  7. +1 −1 {app → freeze-app}/src/main/AndroidManifest.xml
  8. +3 −3 ...boxsdk/MainActivity.java → freeze-app/src/main/java/com/catchingnow/iceboxsdk/FreezeActivity.java
  9. +192 −0 freeze-app/src/main/java/com/catchingnow/iceboxsdk/ShortcutMetaUtil.java
  10. 0 {app → freeze-app}/src/main/res/drawable-v24/ic_launcher_foreground.xml
  11. 0 {app → freeze-app}/src/main/res/drawable/ic_launcher_background.xml
  12. +1 −1 {app → freeze-app}/src/main/res/layout/activity_main.xml
  13. 0 {app → freeze-app}/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  14. 0 {app → freeze-app}/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  15. BIN {app → freeze-app}/src/main/res/mipmap-hdpi/ic_launcher.png
  16. BIN {app → freeze-app}/src/main/res/mipmap-hdpi/ic_launcher_round.png
  17. BIN {app → freeze-app}/src/main/res/mipmap-mdpi/ic_launcher.png
  18. BIN {app → freeze-app}/src/main/res/mipmap-mdpi/ic_launcher_round.png
  19. BIN {app → freeze-app}/src/main/res/mipmap-xhdpi/ic_launcher.png
  20. BIN {app → freeze-app}/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  21. BIN {app → freeze-app}/src/main/res/mipmap-xxhdpi/ic_launcher.png
  22. BIN {app → freeze-app}/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  23. BIN {app → freeze-app}/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  24. BIN {app → freeze-app}/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  25. 0 {app → freeze-app}/src/main/res/values/colors.xml
  26. 0 {app → freeze-app}/src/main/res/values/strings.xml
  27. 0 {app → freeze-app}/src/main/res/values/styles.xml
  28. +2 −2 gradle/wrapper/gradle-wrapper.properties
  29. +1 −0 install-app/.gitignore
  30. +44 −0 install-app/build.gradle
  31. +21 −0 install-app/proguard-rules.pro
  32. +26 −0 install-app/src/androidTest/java/com/catchingnow/installapp/ExampleInstrumentedTest.java
  33. +35 −0 install-app/src/main/AndroidManifest.xml
  34. +50 −0 install-app/src/main/java/com/catchingnow/installapp/MainActivity.java
  35. +34 −0 install-app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  36. +171 −0 install-app/src/main/res/drawable/ic_launcher_background.xml
  37. +45 −0 install-app/src/main/res/layout/activity_main.xml
  38. +5 −0 install-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  39. +5 −0 install-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  40. BIN install-app/src/main/res/mipmap-hdpi/ic_launcher.png
  41. BIN install-app/src/main/res/mipmap-hdpi/ic_launcher_round.png
  42. BIN install-app/src/main/res/mipmap-mdpi/ic_launcher.png
  43. BIN install-app/src/main/res/mipmap-mdpi/ic_launcher_round.png
  44. BIN install-app/src/main/res/mipmap-xhdpi/ic_launcher.png
  45. BIN install-app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  46. BIN install-app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  47. BIN install-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  48. BIN install-app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  49. BIN install-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  50. +6 −0 install-app/src/main/res/values/colors.xml
  51. +3 −0 install-app/src/main/res/values/strings.xml
  52. +11 −0 install-app/src/main/res/values/styles.xml
  53. +4 −0 install-app/src/main/res/xml/file_paths.xml
  54. +17 −0 install-app/src/test/java/com/catchingnow/installapp/ExampleUnitTest.java
  55. +1 −1 sdk-client/src/main/java/com/catchingnow/icebox/sdk_client/AuthorizeUtil.java
  56. +84 −10 sdk-client/src/main/java/com/catchingnow/icebox/sdk_client/IceBox.java
  57. +17 −0 sdk-client/src/main/java/com/catchingnow/icebox/sdk_client/ResultReceiverUtil.java
  58. +0 −4 sdk-client/src/main/java/com/catchingnow/icebox/sdk_client/StateReceiver.java
  59. +1 −1 settings.gradle
@@ -0,0 +1,29 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
</code_scheme>
</component>
@@ -3,7 +3,8 @@
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/IceBoxSDK.iml" filepath="$PROJECT_DIR$/IceBoxSDK.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
<module fileurl="file://$PROJECT_DIR$/freeze-app/freeze-app.iml" filepath="$PROJECT_DIR$/freeze-app/freeze-app.iml" />
<module fileurl="file://$PROJECT_DIR$/install-app/install-app.iml" filepath="$PROJECT_DIR$/install-app/install-app.iml" />
<module fileurl="file://$PROJECT_DIR$/sdk-client/sdk-client.iml" filepath="$PROJECT_DIR$/sdk-client/sdk-client.iml" />
</modules>
</component>
@@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.novoda:bintray-release:0.5.0'


File renamed without changes.
@@ -31,4 +31,6 @@ dependencies {

implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'

api 'io.reactivex.rxjava2:rxjava:2.1.7'
}
File renamed without changes.
@@ -10,7 +10,7 @@
android:supportsRtl="true"
android:theme="@style/AppTheme">

<activity android:name="com.catchingnow.iceboxsdk.MainActivity">
<activity android:name="com.catchingnow.iceboxsdk.FreezeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

@@ -13,7 +13,7 @@
import com.catchingnow.icebox.sdk_client.IceBox;
import com.catchingnow.iceboxsdktestapp.R;

public class MainActivity extends AppCompatActivity {
public class FreezeActivity extends AppCompatActivity {

// 测试包名随便写
private static final String TEST_PACKAGE = "com.supercell.clashroyale";
@@ -38,8 +38,8 @@ protected void onResume() {
super.onResume();
// 如果没有权限,则先请求权限
// 走的标准系统流程
if (ContextCompat.checkSelfPermission(this, IceBox.PERMISSION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{IceBox.PERMISSION}, 0x233);
if (ContextCompat.checkSelfPermission(this, IceBox.SDK_PERMISSION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{IceBox.SDK_PERMISSION}, 0x233);
}

IceBox.WorkMode workMode = IceBox.queryWorkMode(this);
@@ -0,0 +1,192 @@
package com.catchingnow.iceboxsdk;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import android.util.Xml;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import io.reactivex.Observable;

import static android.content.pm.PackageManager.GET_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.GET_META_DATA;
import static android.content.pm.PackageManager.GET_UNINSTALLED_PACKAGES;

public class ShortcutMetaUtil {

private static final String NAME = "android.app.shortcuts";

@WorkerThread
public static List<AppShortcutModel> readShortcut(Context context, String packageName) throws Exception {
PackageManager pm = context.getPackageManager();
ApplicationInfo ai = pm.getApplicationInfo(packageName, GET_UNINSTALLED_PACKAGES);
Resources appRes = pm.getResourcesForApplication(ai);
return Observable.merge(readApplicationLevelMeta(pm, ai),
readActivityLevelMeta(pm, ai))
.flatMap(meta -> {
int resId = meta.getInt(NAME);
if (resId == 0) return Observable.empty();
try {
return Observable.just(appRes.getXml(resId));
} catch (Throwable e) {
e.printStackTrace();
return Observable.empty();
}
})
.flatMap(xml -> {
List<AppShortcutModel> modelList = new ArrayList<>();
int event = xml.getEventType();
while (event != XmlPullParser.END_DOCUMENT){
switch (event){
case XmlPullParser.START_TAG:
if ("shortcut".equals(xml.getName())) {
AppShortcutModel model = readXml2Model(appRes, xml, ai);
if (model != null) modelList.add(model);
}
break;
case XmlPullParser.TEXT:
break;
case XmlPullParser.END_TAG:
break;
default:
break;
}
event = xml.next();
}
return Observable.fromIterable(modelList);
})
.toList()
.blockingGet();
}

private static Observable<Bundle> readActivityLevelMeta(PackageManager pm, ApplicationInfo appInfo) {
return Observable.fromCallable(() -> {
Intent intent = new Intent(Intent.ACTION_MAIN, null).setPackage(appInfo.packageName)
.addCategory(Intent.CATEGORY_LAUNCHER);
return pm.queryIntentActivities(intent, GET_UNINSTALLED_PACKAGES | GET_DISABLED_COMPONENTS | GET_META_DATA);
})
.flatMap(Observable::fromIterable)
.filter(ri -> ri.activityInfo.metaData != null)
.map(ri -> ri.activityInfo.metaData);
}

private static Observable<Bundle> readApplicationLevelMeta(PackageManager pm, ApplicationInfo ai) {
return Observable.fromCallable(() -> pm.getApplicationInfo(ai.packageName, GET_UNINSTALLED_PACKAGES | GET_META_DATA))
.filter(a -> a.metaData != null)
.map(a -> a.metaData);
}

private static AppShortcutModel readXml2Model(Resources res, XmlResourceParser xml, ApplicationInfo ai) {
try {
List<Intent> intentList = new ArrayList<>();
String shortcutId = ai.packageName;
String shortLabel = null;
String longLabel = null;
Drawable icon = null;

int event = xml.getEventType(); //先获取当前解析器光标在哪
while (!(event == XmlPullParser.END_TAG && "shortcut".equals(xml.getName()))){
switch (event){
case XmlPullParser.START_TAG:
switch (xml.getName()) {
case "shortcut":
shortcutId = readAndroidString(res, xml, "shortcutId");
shortLabel = readAndroidString(res, xml, "shortcutShortLabel");
longLabel = readAndroidString(res, xml, "shortcutLongLabel");
icon = readAndroidDrawable(res, xml, "icon");
break;
case "intent":
try {
Intent intent = Intent.parseIntent(res, xml, Xml.asAttributeSet(xml));
intentList.add(intent);
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
}
break;
case "categories":
default:
break;
}
break;
case XmlPullParser.TEXT:
break;
case XmlPullParser.END_TAG:
break;
default:
break;
}
event = xml.next(); //将当前解析器光标往下一步移
}


AppShortcutModel appShortcutModel = new AppShortcutModel();

appShortcutModel.drawable = icon;
appShortcutModel.originalId =shortcutId;
appShortcutModel.shortLabel = shortLabel;
appShortcutModel.longLabel = longLabel;
appShortcutModel.intents = intentList;

return appShortcutModel;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

@Nullable
private static String readAndroidString(Resources res, XmlResourceParser xml, String attrName) {
String value = xml.getAttributeValue("http://schemas.android.com/apk/res/android", attrName);
if (TextUtils.isEmpty(value)) return null;
try {
Integer id = Integer.valueOf(value.replace("@", ""));
return res.getString(id);
} catch (Exception e) {
if (!(e instanceof NumberFormatException)) e.printStackTrace();
return value;
}
}

@Nullable
private static Drawable readAndroidDrawable(Resources res, XmlResourceParser xml, String attrName) {
String value = xml.getAttributeValue("http://schemas.android.com/apk/res/android", attrName);
if (TextUtils.isEmpty(value)) return null;
try {
Integer id = Integer.valueOf(value.replace("@", ""));
return res.getDrawable(id);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

public static class AppShortcutModel {

public String originalId;
public String shortLabel;
public String longLabel;
public List<Intent> intents = new ArrayList<>();
public Drawable drawable;

@Nullable
public Intent getIntent() {
return intents.size() > 0 ? intents.get(0) : null;
}

}

}
@@ -5,7 +5,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.catchingnow.iceboxsdk.MainActivity">
tools:context="com.catchingnow.iceboxsdk.FreezeActivity">

<TextView
tools:text="MODE_PM_HIDE"
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,6 +1,6 @@
#Mon Mar 19 22:24:32 CST 2018
#Mon Nov 05 23:52:18 CST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
@@ -0,0 +1 @@
/build
@@ -0,0 +1,44 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 28

dataBinding {
enabled = true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId "com.catchingnow.installapp"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(":sdk-client")

implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'io.reactivex.rxjava2:rxjava:2.1.7'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,26 @@
package com.catchingnow.installapp;

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;

/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();

assertEquals("com.catchingnow.installapp", appContext.getPackageName());
}
}

0 comments on commit d79a2d9

Please sign in to comment.
You can’t perform that action at this time.