需求
通过其它方式安装App时,提示安装我们应用市场的包。
实现
通过拦截PackageInstaller实现,推荐安装官方应用市场的包
以下给出代码片段,这里没有什么技术难点,主要逻辑在PackageInstallerActivity.java 中,不过JSONLoader.java和JSONRequest.java 里面用到了异步线程和回调。是异步任务的常用方式,可以参考一下。
Coding
diff --git a/res/layout/install_confirm.xml b/res/layout/install_confirm.xml
index a048755..e3b95be 100644
--- a/res/layout/install_confirm.xml
+++ b/res/layout/install_confirm.xml
@@ -128,7 +128,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:showDividers="beginning"
- android:layout_marginTop="@dimen/dimen_68px"
+ android:layout_marginTop="@dimen/dimen_38px"
>
<TextView
@@ -143,6 +143,18 @@
android:layout_marginBottom="@dimen/dimen_10px"
android:layout_gravity="center" />
+ <TextView
+ android:id="@+id/official_warning"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/install_official_version_tips"
+ android:textSize="@dimen/dimen_36px"
+ android:textColor="#ccff0000"
+ android:shadowColor="@color/shadow"
+ android:visibility="gone"
+ android:layout_marginBottom="@dimen/dimen_10px"
+ android:layout_gravity="center" />
+
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -156,6 +168,16 @@
<!-- android:orientation="horizontal" -->
<!-- android:visibility="gone" /> -->
+ <Button android:id="@+id/ok_official_button"
+ style="@android:style/FunDialogButton"
+ android:layout_width="@android:dimen/fun_dialog_button_width"
+ android:layout_height="@android:dimen/fun_dialog_button_height"
+ android:layout_gravity="center_horizontal"
+ android:filterTouchesWhenObscured="false"
+ android:maxLines="2"
+ android:text="@string/install_official_version"
+ android:visibility="gone" />
+
<!-- MStar Android Patch Begin -->
<Button android:id="@+id/ok_button"
style="@android:style/FunDialogButton"
diff --git a/res/layout/install_start.xml b/res/layout/install_start.xml
index 38a4834..bd73fe6 100644
--- a/res/layout/install_start.xml
+++ b/res/layout/install_start.xml
@@ -20,11 +20,19 @@
android:layout_height="match_parent"
>
+ <ProgressBar android:id="@+id/install_start_profress"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:visibility="visible"/>
+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/install_start_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:visibility="gone" >
<include
android:id="@+id/app_snippet"
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 9d28760..149c2cc 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -81,4 +81,7 @@
<string name="no_new_perms" msgid="6657813692169565975">"新版本不需要任何新的权限。"</string>
<string name="grant_confirm_question" msgid="4690289297029223742">"要向该应用授予以下权限吗?"</string>
<string name="install_failed_version_downgrade">"已安装此应用更高版本"</string>
+
+ <string name="install_official_version">"安装官方版本"</string>
+ <string name="install_official_version_tips">"建议安装风行官方应用市场的应用,安全快速"</string>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6a28dbf..fbdffa7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -150,5 +150,7 @@
<string name="cancel2">取\u0020\u0020消</string>
<string name="submit">确\u0020\u0020定</string>
<string name="neutral">设\u0020\u0020置</string>
-
+
+ <string name="install_official_version">"安装官方版本"</string>
+ <string name="install_official_version_tips">"建议安装风行官方应用市场的应用,安全快速"</string>
</resources>
diff --git a/src/com/android/packageinstaller/PackageInstallerActivity.java b/src/com/android/packageinstaller/PackageInstallerActivity.java
index 443e9e1..34ae134 100644
--- a/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -18,7 +18,6 @@ package com.android.packageinstaller;
/*
@@ -111,6 +118,26 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
private static final int DLG_ALLOW_SOURCE = DLG_BASE + 5;
+ private ProgressBar mProgressBar;
+ private LinearLayout mInstallStartContent;
+ private TextView mOfficialWarning;
+ private Button mOkOfficialButton;
+ private static final int SHOW_UI = 111;
+ private static final int NOTIFY_SHOW_UI = 15 * 1000;
+
+ private Handler mUIHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (SHOW_UI == msg.what) {
+ if (mProgressBar != null && mInstallStartContent != null) {
+ mProgressBar.setVisibility(View.GONE);
+ mInstallStartContent.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+ };
+
+
private void startInstallConfirm() {
TabHost tabHost = (TabHost) findViewById(android.R.id.tabhost);
tabHost.setup();
@@ -517,6 +544,14 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
//set view
setContentView(R.layout.install_start);
+ mProgressBar = (ProgressBar)findViewById(R.id.install_start_profress);
+ mInstallStartContent = (LinearLayout)findViewById(R.id.install_start_content);
+ mOfficialWarning = (TextView)findViewById(R.id.official_warning);
+ mOkOfficialButton = (Button)findViewById(R.id.ok_official_button);
+ loadAppInfo();
+ mUIHandler.sendEmptyMessageDelayed(SHOW_UI, NOTIFY_SHOW_UI);
+
+
final File data = new File("/data");
final StorageManager sm = (StorageManager)getSystemService(STORAGE_SERVICE);
final long lowBytes = sm.getStorageLowBytes(data) + DEFAULT_THRESHOLD_BYTES;
@@ -670,6 +705,88 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);
finish();
+ } else if(v == mOkOfficialButton) {
+ redirect2FunAppStore();
}
}
+
+ private void loadAppInfo() {
+ String pkgName = mPkgInfo.packageName;
+ int versioncode = mPkgInfo.versionCode;
+ Log.i(TAG, "loadAppInfo pkgName:" + pkgName + ";versioncode:" + versioncode);
+ submitJSONRequest(new JSONRequest(pkgName, versioncode), "onGetAppData");
+ }
+
+ private void submitJSONRequest(JSONRequest request, String cbName) {
+ request.setCallback(getCallback(cbName));
+ JSONLoader.submit(request);
+ }
+
+ private JSONCallback getCallback(final String name) {
+ JSONCallback cb = new JSONCallback() {
+ @Override
+ public boolean isAlive() {
+ return !isDestroyed();
+ }
+
+ @Override
+ public void onResult(JSONObject json) {
+ try {
+ Activity host = PackageInstallerActivity.this;
+ Method m = host.getClass().getMethod(name, JSONObject.class);
+ m.invoke(host, json);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ };
+ return cb;
+ }
+
+ public void onGetAppData(JSONObject obj) {
+ mUIHandler.removeMessages(SHOW_UI);
+ mProgressBar.setVisibility(View.GONE);
+ mInstallStartContent.setVisibility(View.VISIBLE);
+ if (obj == null) return;
+ Log.i(TAG, "onGetAppData json=" + obj.toString());
+ try {
+ int retCode = obj.getInt("retCode");
+ if (retCode == 200) {
+ showOfficialMenu();
+ } else {
+ hideOfficialMenu();
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ hideOfficialMenu();
+ }
+ }
+
+ private void showOfficialMenu(){
+ mOfficialWarning.setVisibility(View.VISIBLE);
+ mOkOfficialButton.setVisibility(View.VISIBLE);
+ mOkOfficialButton.requestFocus();
+ mOkOfficialButton.setOnClickListener(this);
+ }
+
+ private void hideOfficialMenu(){
+ mOfficialWarning.setVisibility(View.GONE);
+ mOkOfficialButton.setVisibility(View.GONE);
+ mOk.requestFocus();
+ }
+
+ private void redirect2FunAppStore(){
+ Intent intent = new Intent("tv.fun.appstore.view.app");
+ intent.putExtra("mode", 1);
+ intent.putExtra("packageName", mPkgInfo.packageName);
+ intent.putExtra("fromInstaller", true);
+ startActivity(intent);
+ finish();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ finish();
+ }
}
JSONCallback.java
import org.json.JSONObject;
public interface JSONCallback {
boolean isAlive();
void onResult(JSONObject json);
}
JSONRequest.java
import android.util.Log;
import java.lang.ref.WeakReference;
import org.json.JSONException;
import org.json.JSONObject;
public class JSONRequest {
private String TAG = JSONRequest.class.getSimpleName();
private int tryTimes = 3;
private String mPackageName;
private int mVersionCode;
private static String PKGNAME = "pkgName";
private static String VERCODE = "verCode";
private static final String TEST_URL = "xxx";
private static final String RELEASE_URL = "xxxx";
public JSONRequest(String pkg, int versioncode) {
mPackageName = pkg;
mVersionCode = versioncode;
}
/**
* JSON callback handler
*/
private JSONCallback mCb;
public void setCallback(JSONCallback callback) {
mCb = callback;
}
private String getUrl() {
//return TEST_URL;
return RELEASE_URL;
}
public JSONObject getParams() throws JSONException {
JSONObject obj = new JSONObject();
obj.put(PKGNAME, mPackageName);
obj.put(VERCODE, mVersionCode);
return obj;
}
/**
* Called in work thread.
*
* @return
*/
public JSONObject onExecute() {
JSONObject ret = null;
try {
ret = requestJSON();
} catch (JSONException e) {
e.printStackTrace();
}
return ret;
}
/**
* Called in UI thread.
*
* @param
*/
public void onResult(JSONObject obj) throws JSONException {
if (mCb == null || !mCb.isAlive()) {
Log.w(TAG, "JSON callback is gone");
return;
}
mCb.onResult(obj);
}
private JSONObject requestJSON() throws JSONException {
String json = null;
String url = getUrl();
JSONObject jsonParams = getParams();
String params = jsonParams.toString();
for (int i = 0; i < tryTimes; i++) {
try {
json = HttpUtil.requestJson(url, params);
break;
} catch (Exception e) {
e.printStackTrace();
}
}
try {
return new JSONObject(json);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
JSONLoader.java
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class JSONLoader extends AsyncTask<JSONRequest, Void, JSONObject> {
private static final ExecutorService mExecutor = Executors.newCachedThreadPool();
private JSONRequest mRequest;
private String TAG = "JSONLoader";
public static Executor getExecutor() {
return mExecutor;
}
public static void submit(JSONRequest request) {
JSONLoader loader = new JSONLoader();
loader.executeOnExecutor(mExecutor, request);//开启线程-request 作为参数传递到线程中
}
@Override
protected JSONObject doInBackground(JSONRequest... params) {
try {
mRequest = params[0];//拿到JSONRequest 对象
// Common entry point for all json request.
return mRequest.onExecute();//执行JSONRequest 中下载任务
} catch (Exception e) {
Log.e(TAG, "doInBackgroundException:" + e);
}
return null;
}
@Override
protected void onPostExecute(JSONObject json) {
try {
mRequest.onResult(json);//返回结果,JSONRequest 中回调
} catch (JSONException e) {
e.printStackTrace();
}
}
}
HttpUtil.java
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public final class HttpUtil {
public static final int CONNECTION_TIMEOUT = 10 * 1000;
public static String requestJson(String url, String jsonParams) throws IOException {
InputStream is = null;
OutputStream os = null;
try {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setInstanceFollowRedirects(true);
conn.setReadTimeout(CONNECTION_TIMEOUT);
conn.setConnectTimeout(CONNECTION_TIMEOUT);
conn.setRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod("POST");
conn.connect();
os = conn.getOutputStream();
os.write(jsonParams.getBytes());
os.close();
int statusCode = conn.getResponseCode();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (statusCode == HttpURLConnection.HTTP_OK) {
is = conn.getInputStream();
int cnt;
final byte[] buf = new byte[8192];
while ((cnt = is.read(buf, 0, buf.length)) != -1) {
baos.write(buf, 0, cnt);
}
} else {
return null;
}
return baos.toString();
} finally {
close(is);
close(os);
}
}
public static void close(Closeable close) {
try {
if (close != null)
close.close();
} catch (Exception e) {
}
}
}
技术点
AsyncTask & 设置回调等