Repository: lzyickobe/UnInstallDemo
Branch: master
Commit: 12ca0e74e339
Files: 14
Total size: 20.0 KB
Directory structure:
gitextract_6sfa6kaj/
├── .gitattributes
├── .gitignore
├── .project
├── AndroidManifest.xml
├── README.md
├── jni/
│ ├── Android.mk
│ └── UninstalledObserver.c
├── libs/
│ └── android-support-v4.jar
├── proguard-project.txt
├── project.properties
├── res/
│ ├── layout/
│ │ └── activity_main.xml
│ └── values/
│ ├── dimens.xml
│ └── strings.xml
└── src/
└── com/
└── lzyblog/
└── uninstalldemo/
└── UninstalledObserverActivity.java
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
================================================
FILE: .gitignore
================================================
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# =========================
# Operating System Files
# =========================
# OSX
# =========================
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
================================================
FILE: .project
================================================
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>UnInstallDemo</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>auto,full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value><project>/.externalToolBuilders/New_NDK.launch</value>
</dictionary>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
================================================
FILE: AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lzyblog.uninstalldemo"
android:versionCode="1"
android:versionName="1.1" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".UninstalledObserverActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
================================================
FILE: README.md
================================================
## Android应用监听自己是否被卸载,卸载后弹出反馈网页
### 在前人的基础上有这些解决方案
1. 监听卸载广播,只能监听到别人卸载。自己被卸载的时候,早就收不到广播了。
2. 监听log。这样听起来很靠谱,能稳定监听到,但是发送操作不靠谱。
3. 监听/data/data/<package name>。当Android卸载应用的时候,会先删除这里的文件。可以轮询监听,可以优化成unix文件监听方式,,这样只用等待文件监听服务的回调。
### 采用了第3种解决办法,并对其进行了优化:
#### 问题:
监听/data/data/<package name>这个目录,还存在以下几个问题:
1. 清除数据、插拔USB线、覆盖安装等操作引起程序误判卸载。
2. 重复监听的问题。
3. 用户将已在Internal SD卡安装好的应用移动到external SD卡,导致监听不正常。
#### 原因:
1. 由于inotify_add_watch(fileDescriptor, path, IN_DELETE)这个函数会监听path目录下所有文件的删除操作导致。
2. 重复调用JNI的init方法
3. 暂时未修复
#### 解决方法:
1. 监听不应该针对整个文件夹,而是某个文件。
2. 重复监听的问题,都可以通过加文件锁来防止
详细方案可参考我的博文:[Android监听自己是否被卸载](http://lzyblog.com/2015/01/09/Android%E5%BA%94%E7%94%A8%E7%9B%91%E5%90%AC%E8%87%AA%E5%B7%B1%E6%98%AF%E5%90%A6%E8%A2%AB%E5%8D%B8%E8%BD%BD%EF%BC%8C%E5%81%9A%E5%8F%8D%E9%A6%88%E7%BB%9F%E8%AE%A1/)
参考自:
https://github.com/sevenler/Uninstall_Statics
http://www.cnblogs.com/zealotrouge/p/3157126.html
http://www.cnblogs.com/zealotrouge/p/3159772.html
http://www.cnblogs.com/zealotrouge/p/3182617.html
================================================
FILE: jni/Android.mk
================================================
# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := uninstalled_observer
LOCAL_SRC_FILES := UninstalledObserver.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
include $(BUILD_SHARED_LIBRARY)
================================================
FILE: jni/UninstalledObserver.c
================================================
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* NOTICE:cļҪĶֱʶ TODO1 TODO2
*/
#include<jni.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/inotify.h>
#include<sys/stat.h>
#include<android/log.h>
/* 궨begin */
//0
#define MEM_ZERO(pDest, destSize) memset(pDest, 0, destSize)
//LOG궨
#define LOG_INFO(tag, msg) __android_log_write(ANDROID_LOG_INFO, tag, msg)
#define LOG_DEBUG(tag, msg) __android_log_write(ANDROID_LOG_DEBUG, tag, msg)
#define LOG_WARN(tag, msg) __android_log_write(ANDROID_LOG_WARN, tag, msg)
#define LOG_ERROR(tag, msg) __android_log_write(ANDROID_LOG_ERROR, tag, msg)
/* ȫֱbegin */
static char TAG[] = "UninstalledObserverActivity.init";
static jboolean isCopy = JNI_TRUE;
//--TODO1--------------------ҪijԼapkİ------------------------------------
static const char APP_DIR[] = "/data/data/com.lzyblog.uninstalldemo";
static const char APP_FILES_DIR[] = "/data/data/com.lzyblog.uninstalldemo/files";
static const char APP_OBSERVED_FILE[] = "/data/data/com.lzyblog.uninstalldemo/files/observedFile";
static const char APP_LOCK_FILE[] = "/data/data/com.lzyblog.uninstalldemo/files/lockFile";
/* ȫֱ */
/*-------TODO2-----------------------
* Class: ҪijԼapp
* Method: init
* Signature: ()V
* return: ӽpid
*/
JNIEXPORT int JNICALL Java_com_lzyblog_uninstalldemo_UninstalledObserverActivity_init(JNIEnv *env, jobject obj, jstring userSerial, jstring website)
{
jstring tag = (*env)->NewStringUTF(env, TAG);
char* websiteStr=(char*) (*env)->GetStringUTFChars(env, website, NULL);
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "init observer"), &isCopy));
// forkӽִ̣ѯ
pid_t pid = fork();
if (pid < 0)
{
LOG_ERROR((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "fork failed !!!"), &isCopy));
exit(1);
}
else if (pid == 0)
{
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "fork Success !!!"), &isCopy));
// ļļвڣ
FILE *p_filesDir = fopen(APP_FILES_DIR, "r");
if (p_filesDir == NULL)
{
int filesDirRet = mkdir(APP_FILES_DIR, S_IRWXU | S_IRWXG | S_IXOTH);
if (filesDirRet == -1)
{
LOG_ERROR((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "mkdir failed !!!"), &isCopy));
exit(1);
}
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "mkdir Success !!!"), &isCopy));
}
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "app dir is exist !!!"), &isCopy));
// ļڣļ
FILE *p_observedFile = fopen(APP_OBSERVED_FILE, "r");
if (p_observedFile == NULL)
{
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "the observed file is not existed!!!"), &isCopy));
p_observedFile = fopen(APP_OBSERVED_FILE, "w");
if (p_observedFile == NULL) {
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "the observed file create falied!!!"), &isCopy));
}
}
fclose(p_observedFile);
// ļͨ״ֻ̬֤һжؼ
int lockFileDescriptor = open(APP_LOCK_FILE, O_RDONLY);
if (lockFileDescriptor == -1)
{
lockFileDescriptor = open(APP_LOCK_FILE, O_CREAT);
}
int lockRet = flock(lockFileDescriptor, LOCK_EX | LOCK_NB);
if (lockRet == -1)
{
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "observed by another process"), &isCopy));
exit(0);
}
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "observed by child process"), &isCopy));
// ռ䣬Աȡevent
void *p_buf = malloc(sizeof(struct inotify_event));
if (p_buf == NULL)
{
LOG_ERROR((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "malloc failed !!!"), &isCopy));
exit(1);
}
// ռ䣬Աӡmask
int maskStrLength = 7 + 10 + 1;// mask=0xռ7ֽڣ32λΪ10λתΪַռ10ֽڣ'\0'ռ1ֽ
char *p_maskStr = malloc(maskStrLength);
if (p_maskStr == NULL)
{
free(p_buf);
LOG_ERROR((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "malloc failed !!!"), &isCopy));
exit(1);
}
// ʼ
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "start observe"), &isCopy));
// ʼ
int fileDescriptor = inotify_init();
if (fileDescriptor < 0)
{
free(p_buf);
free(p_maskStr);
LOG_ERROR((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "inotify_init failed !!!"), &isCopy));
exit(1);
}
// ӱļб
int watchDescriptor = inotify_add_watch(fileDescriptor, APP_OBSERVED_FILE, IN_ALL_EVENTS);
if (watchDescriptor < 0)
{
free(p_buf);
free(p_maskStr);
LOG_ERROR((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "inotify_add_watch failed !!!"), &isCopy));
exit(1);
}
while(1)
{
// read
size_t readBytes = read(fileDescriptor, p_buf, sizeof(struct inotify_event));
// ӡmask
snprintf(p_maskStr, maskStrLength, "mask=0x%x\0", ((struct inotify_event *) p_buf)->mask);
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, p_maskStr), &isCopy));
if (IN_DELETE_SELF == ((struct inotify_event *) p_buf)->mask)
{
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "the observer file is deleted"), &isCopy));
inotify_rm_watch(fileDescriptor, watchDescriptor);
break;
//ʵжģȷĸжأĸݣ
/*
FILE *p_appDir = fopen(APP_DIR, "r");
// ȷж
if (p_appDir == NULL)
{
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "app is uninstall"), &isCopy));
inotify_rm_watch(fileDescriptor, watchDescriptor);
break;
}
// δжأûִ""
else
{
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "app not uninstall , the data is empty"), &isCopy));
fclose(p_appDir);
// ´ļ¼
FILE *p_observedFile = fopen(APP_OBSERVED_FILE, "w");
fclose(p_observedFile);
int watchDescriptor = inotify_add_watch(fileDescriptor, APP_OBSERVED_FILE, IN_ALL_EVENTS);
if (watchDescriptor < 0)
{
free(p_buf);
free(p_maskStr);
LOG_ERROR((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "inotify_add_watch failed !!!"), &isCopy));
exit(1);
}
}
*/
}
}
// ͷԴ
free(p_buf);
free(p_maskStr);
// ֹͣ
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "stop observe"), &isCopy));
if (userSerial == NULL)
{
// ִam start -a android.intent.action.VIEW -d $(url)
execlp("am", "am", "start", "-a", "android.intent.action.VIEW", "-d", websiteStr, (char *)NULL);
}
else
{
// ִam start --user userSerial -a android.intent.action.VIEW -d $(url)
execlp("am", "am", "start", "--user", (*env)->GetStringUTFChars(env, userSerial, &isCopy), "-a", "android.intent.action.VIEW", "-d", websiteStr, (char *)NULL);
}
// ִʧlog
LOG_ERROR((*env)->GetStringUTFChars(env, tag, &isCopy)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "exec AM command failed !!!"), &isCopy));
(*env)->ReleaseStringUTFChars(env, website, websiteStr);
}
else
{
// ֱ˳ʹӽ̱initԱӽ̽ͬʱӽpid
(*env)->ReleaseStringUTFChars(env, website, websiteStr);
return pid;
}
}
================================================
FILE: proguard-project.txt
================================================
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# 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 *;
#}
================================================
FILE: project.properties
================================================
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-19
================================================
FILE: res/layout/activity_main.xml
================================================
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
</RelativeLayout>
================================================
FILE: res/values/dimens.xml
================================================
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>
================================================
FILE: res/values/strings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">UnInstallDemo</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
</resources>
================================================
FILE: src/com/lzyblog/uninstalldemo/UninstalledObserverActivity.java
================================================
package com.lzyblog.uninstalldemo;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
/**
* @author liuzhiyong תpengyiming
* @note ӦǷжأжжط
* @note API17û֧֣ԭ4.2߰汾ִʱȱuserSerialش
* @note ˴δᵽһЩbugݡUSBߡǰװȲжء
* @note κظ⣬ͨļֹpsȡؽ˽ķҪúܶࡣ
* @note װSDжؼȻû⣬ûInternal SDװõӦƶexternal
* SD.c161δfilesļкļӦûbug붼УҪbugɡ
* @note ͵ַ:http://lzyblog.com
*/
public class UninstalledObserverActivity extends Activity {
/* ݶbegin */
private static final String TAG = "UninstalledObserverActivity";
//վ
private static final String WEBSITE = "http://lzyblog.com";
// pid
private int mObserverProcessPid = -1;
/* ݶend */
/* static */
// ʼ
private native int init(String userSerial, String webSite);
static {
Log.d(TAG, "load lib --> uninstalled_observer");
System.loadLibrary("uninstalled_observer");
}
/* static */
/* begin */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
createFile();
// API levelС17ҪȡuserSerialNumber
if (Build.VERSION.SDK_INT < 17) {
mObserverProcessPid = init(null, WEBSITE);
}
// ҪȡuserSerialNumber
else {
mObserverProcessPid = init(getUserSerial(), WEBSITE);
}
}
private void createFile() {
File file = new File("/data/data/com.lzyblog.uninstalldemo/files/observedFile");
if (!file.exists()) {
try {
File dir = new File("/data/data/com.lzyblog.uninstalldemo/files");
if (!dir.exists()) {
if (dir.mkdir()) {
Log.e(TAG, "filesĿ¼ɹ");
} else {
Log.e(TAG, "filesĿ¼ʧ");
return;
}
}
if (file.createNewFile()) {
Log.e(TAG, "observedFileɹ");
return;
}
Log.e(TAG, "observedFileʧ");
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "observedFileʧ");
}
} else {
Log.e(TAG, "observedFile");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// ʾ룬ڽ
// if (mObserverProcessPid > 0)
// {
// android.os.Process.killProcess(mObserverProcessPid);
// }
}
// targetSdkVersion17ֻͨȡ
private String getUserSerial() {
Object userManager = getSystemService("user");
if (userManager == null) {
Log.e(TAG, "userManager not exsit !!!");
return null;
}
try {
Method myUserHandleMethod = android.os.Process.class.getMethod(
"myUserHandle", (Class<?>[]) null);
Object myUserHandle = myUserHandleMethod.invoke(
android.os.Process.class, (Object[]) null);
Method getSerialNumberForUser = userManager.getClass().getMethod(
"getSerialNumberForUser", myUserHandle.getClass());
long userSerial = (Long) getSerialNumberForUser.invoke(userManager,
myUserHandle);
return String.valueOf(userSerial);
} catch (NoSuchMethodException e) {
Log.e(TAG, "", e);
} catch (IllegalArgumentException e) {
Log.e(TAG, "", e);
} catch (IllegalAccessException e) {
Log.e(TAG, "", e);
} catch (InvocationTargetException e) {
Log.e(TAG, "", e);
}
return null;
}
/* end */
}
gitextract_6sfa6kaj/
├── .gitattributes
├── .gitignore
├── .project
├── AndroidManifest.xml
├── README.md
├── jni/
│ ├── Android.mk
│ └── UninstalledObserver.c
├── libs/
│ └── android-support-v4.jar
├── proguard-project.txt
├── project.properties
├── res/
│ ├── layout/
│ │ └── activity_main.xml
│ └── values/
│ ├── dimens.xml
│ └── strings.xml
└── src/
└── com/
└── lzyblog/
└── uninstalldemo/
└── UninstalledObserverActivity.java
SYMBOL INDEX (7 symbols across 2 files)
FILE: jni/UninstalledObserver.c
function JNICALL (line 58) | JNICALL Java_com_lzyblog_uninstalldemo_UninstalledObserverActivity_init(...
FILE: src/com/lzyblog/uninstalldemo/UninstalledObserverActivity.java
class UninstalledObserverActivity (line 24) | public class UninstalledObserverActivity extends Activity {
method init (line 38) | private native int init(String userSerial, String webSite);
method onCreate (line 48) | @Override
method createFile (line 67) | private void createFile() {
method onDestroy (line 94) | @Override
method getUserSerial (line 106) | private String getUserSerial() {
Condensed preview — 14 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (23K chars).
[
{
"path": ".gitattributes",
"chars": 483,
"preview": "# Auto detect text files and perform LF normalization\n* text=auto\n\n# Custom for Visual Studio\n*.cs diff=csharp\n*.sln"
},
{
"path": ".gitignore",
"chars": 606,
"preview": "# Windows image file caches\r\nThumbs.db\r\nehthumbs.db\r\n\r\n# Folder config file\r\nDesktop.ini\r\n\r\n# Recycle Bin used on file s"
},
{
"path": ".project",
"chars": 1137,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>UnInstallDemo</name>\n\t<comment></comment>\n\t<projects>"
},
{
"path": "AndroidManifest.xml",
"chars": 817,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "README.md",
"chars": 1008,
"preview": "## Android应用监听自己是否被卸载,卸载后弹出反馈网页\n\n### 在前人的基础上有这些解决方案\n1. 监听卸载广播,只能监听到别人卸载。自己被卸载的时候,早就收不到广播了。\n2. 监听log。这样听起来很靠谱,能稳定监听到,但是发送"
},
{
"path": "jni/Android.mk",
"chars": 855,
"preview": "# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");"
},
{
"path": "jni/UninstalledObserver.c",
"chars": 10264,
"preview": "\n/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "proguard-project.txt",
"chars": 781,
"preview": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in t"
},
{
"path": "project.properties",
"chars": 563,
"preview": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# T"
},
{
"path": "res/layout/activity_main.xml",
"chars": 377,
"preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tools=\"http://schemas.android.com/t"
},
{
"path": "res/values/dimens.xml",
"chars": 213,
"preview": "<resources>\n\n <!-- Default screen margins, per the Android Design guidelines. -->\n <dimen name=\"activity_horizonta"
},
{
"path": "res/values/strings.xml",
"chars": 223,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <string name=\"app_name\">UnInstallDemo</string>\n <string name="
},
{
"path": "src/com/lzyblog/uninstalldemo/UninstalledObserverActivity.java",
"chars": 3189,
"preview": "package com.lzyblog.uninstalldemo;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.Invocation"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the lzyickobe/UnInstallDemo GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 14 files (20.0 KB), approximately 6.1k tokens, and a symbol index with 7 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.