Browse Source

Merge branch 'master' of https://github.com/Bloody-Badboy/Remove-China-Apps

# Conflicts:
#	README.md
liuyuqi-dellpc 3 years ago
parent
commit
194b603e68

+ 12 - 0
.idea/runConfigurations.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RunConfigurationProducerService">
+    <option name="ignoredProducers">
+      <set>
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
+      </set>
+    </option>
+  </component>
+</project>

+ 1 - 1
.idea/vcs.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="VcsDirectoryMappings">
-    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+    <mapping directory="" vcs="Git" />
   </component>
 </project>

+ 10 - 4
README.md

@@ -1,11 +1,17 @@
 
-## \[ Remove China Apps - Reverse Engineering 👷‍️⛏👷🔧️👷🔧 \]
+> Source code of [Remove China Apps](https://play.google.com/store/apps/details?id=com.chinaappsremover), an Android app that claims to identify China-made apps on your Android phone and remove them, that has gone viral in India.
 
+Added the functionality to refresh and cache the chinese app list from the [`china_apps.json`](https://github.com/Bloody-Badboy/Remove-China-Apps/blob/master/china_apps.json) file.
+You can create pull request and update the [`china_apps.json`](https://github.com/Bloody-Badboy/Remove-China-Apps/blob/master/china_apps.json) to blacklist more chinese apps.
 
-> Source code of [Remove China Apps][0], an Android app that claims to identify China-made apps on your Android phone and remove them, has gone viral in India.
+### Requirements
+- Latest Android SDK tools
+- Latest Android platform tools
+- AndroidX
+
+### Build
+    ./gradlew assembleDebug
 
 ### screenshot
 
 ![](screenshot/Screenshot_2020-06-05-08-50-49-493_com.chinaappsremover.jpg)
-
-[0]: https://play.google.com/store/apps/details?id=com.chinaappsremover

+ 4 - 0
app/build.gradle

@@ -19,6 +19,9 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
         }
     }
+    buildTypes.each {
+        it.buildConfigField "String", "BLACKLISTED_APP_JSON_URL", "\"https://raw.githubusercontent.com/Bloody-Badboy/Remove-China-Apps/master/china_apps.json\""
+    }
 }
 
 dependencies {
@@ -32,6 +35,7 @@ dependencies {
     implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
 
     implementation 'com.google.android.play:core:1.7.3'
+    implementation 'com.squareup.okhttp3:okhttp:4.7.2'
 
     testImplementation 'junit:junit:4.13'
     androidTestImplementation 'androidx.test.ext:junit:1.1.1'

+ 5 - 3
app/src/main/AndroidManifest.xml

@@ -2,12 +2,14 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.chinaappsremover">
 
-    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
-    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
+    <uses-permission android:name="android.permission.INTERNET" />
 
     <application
         android:name=".AppController"
         android:allowBackup="true"
+        android:fullBackupContent="false"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
         android:supportsRtl="true"
@@ -19,7 +21,7 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity android:name=".ui.MainActivity"/>
+        <activity android:name=".ui.MainActivity" />
     </application>
 
 </manifest>

+ 518 - 0
app/src/main/assets/china_apps.json

@@ -0,0 +1,518 @@
+[
+  {
+    "p_name": "app.buzz.share",
+    "a_name": "Helo"
+  },
+  {
+    "p_name": "app.buzz.share.lite",
+    "a_name": "Helo Lite"
+  },
+  {
+    "p_name": "com.lenovo.anyshare.gps",
+    "a_name": "SHAREit"
+  },
+  {
+    "p_name": "shareit.lite",
+    "a_name": "SHAREit Lite"
+  },
+  {
+    "p_name": "com.ushareit.watchit",
+    "a_name": "WATCHit"
+  },
+  {
+    "p_name": "com.zhiliaoapp.musically",
+    "a_name": "TikTok"
+  },
+  {
+    "p_name": "com.zhiliao.musically.livewallpaper",
+    "a_name": "TikTok Wall Picture"
+  },
+  {
+    "p_name": "com.ss.android.ugc.trill.go",
+    "a_name": "TikTok Lite"
+  },
+  {
+    "p_name": "com.kwai.video",
+    "a_name": "Kwai"
+  },
+  {
+    "p_name": "com.UCMobile.intl",
+    "a_name": "UCBrowser"
+  },
+  {
+    "p_name": "com.uc.browser.en",
+    "a_name": "UCBrowser Mini"
+  },
+  {
+    "p_name": "com.ucturbo",
+    "a_name": "UC Browser Turbo"
+  },
+  {
+    "p_name": "com.CricChat.intl",
+    "a_name": "UC Cricket"
+  },
+  {
+    "p_name": "com.live.royal",
+    "a_name": "LiveMe"
+  },
+  {
+    "p_name": "sg.bigo.live",
+    "a_name": "Bigo Live"
+  },
+  {
+    "p_name": "com.ss.android.ugc.boom",
+    "a_name": "Vigo Video"
+  },
+  {
+    "p_name": "com.ss.android.ugc.boom.livewallpaper",
+    "a_name": "Vigo Video Live Photo"
+  },
+  {
+    "p_name": "com.magicv.airbrush",
+    "a_name": "AirBrush: Easy Photo Editor"
+  },
+  {
+    "p_name": "com.commsource.beautyplus",
+    "a_name": "BeautyPlus"
+  },
+  {
+    "p_name": "com.meitu.makeup",
+    "a_name": "MakeupPlus"
+  },
+  {
+    "p_name": "com.mt.mtxx.mtxx",
+    "a_name": "Meitu "
+  },
+  {
+    "p_name": "com.meitu.meiyancamera",
+    "a_name": "BeautyCam"
+  },
+  {
+    "p_name": "com.meitu.oxygen",
+    "a_name": "O2Cam"
+  },
+  {
+    "p_name": "com.meitu.airvid",
+    "a_name": "AirVid Video Filters & Frames"
+  },
+  {
+    "p_name": "com.meitu.beautyplusme",
+    "a_name": "PlusMe Camera"
+  },
+  {
+    "p_name": "com.meitu.meipaimv",
+    "a_name": "Meipai"
+  },
+  {
+    "p_name": "com.beautyplus.pomelo.filters.photo",
+    "a_name": "Pomelo "
+  },
+  {
+    "p_name": "com.meitu.wheecam",
+    "a_name": "SelfieCity"
+  },
+  {
+    "p_name": "com.meitu.airbrush.vivo",
+    "a_name": "AirBrush 2Go"
+  },
+  {
+    "p_name": "com.meitu.boxxcam",
+    "a_name": "BOXxCAM"
+  },
+  {
+    "p_name": "cn.xender",
+    "a_name": "Xender"
+  },
+  {
+    "p_name": "com.intsig.camscanner",
+    "a_name": "Cam Scanner"
+  },
+  {
+    "p_name": "com.intsig.BCRLite",
+    "a_name": "CamCard Free"
+  },
+  {
+    "p_name": "com.tencent.ludosuperstar",
+    "a_name": "Ludo World"
+  },
+  {
+    "p_name": "com.ngame.allstar.eu",
+    "a_name": "Arena of Valor"
+  },
+  {
+    "p_name": "com.tencent.godgame",
+    "a_name": "Chess Rush"
+  },
+  {
+    "p_name": "com.hcg.cok.gp",
+    "a_name": "Clash of Kings"
+  },
+  {
+    "p_name": "com.hcg.ctw.gp",
+    "a_name": "Clash of Kings:The West"
+  },
+  {
+    "p_name": "com.elex.coq.gp",
+    "a_name": "Clash of Queens"
+  },
+  {
+    "p_name": "com.zw.zombieworld.gp",
+    "a_name": "Zombie World SLG 3D"
+  },
+  {
+    "p_name": "com.hcg.tos.gp",
+    "a_name": "Heroes War"
+  },
+  {
+    "p_name": "com.mobile.legends",
+    "a_name": "Mobile Legends"
+  },
+  {
+    "p_name": "club.fromfactory",
+    "a_name": "ClubFactory"
+  },
+  {
+    "p_name": "com.zzkko",
+    "a_name": "Shein"
+  },
+  {
+    "p_name": "com.romwe",
+    "a_name": "Romwe"
+  },
+  {
+    "p_name": "com.domobile.applockwatcher",
+    "a_name": "AppLock"
+  },
+  {
+    "p_name": "com.uc.vmate",
+    "a_name": "VMate"
+  },
+  {
+    "p_name": "com.dc.hwsj",
+    "a_name": "Game of Sultans"
+  },
+  {
+    "p_name": "com.yottagames.mafiawar",
+    "a_name": "Mafia City"
+  },
+  {
+    "p_name": "com.tencent.mm",
+    "a_name": "Wechat"
+  },
+  {
+    "p_name": "com.tencent.mobileqq",
+    "a_name": "QQ"
+  },
+  {
+    "p_name": "com.baidu.searchbox",
+    "a_name": "Baidu"
+  },
+  {
+    "p_name": "com.security.antivirus.sefeanti",
+    "a_name": "360 Mobile Guard"
+  },
+  {
+    "p_name": "com.taobao.taobao",
+    "a_name": "Taobao"
+  },
+  {
+    "p_name": "com.sina.weibo",
+    "a_name": "Sina Weibo"
+  },
+  {
+    "p_name": "com.weico.international",
+    "a_name": "Weibo"
+  },
+  {
+    "p_name": "com.uc.iflow",
+    "a_name": "UC News"
+  },
+  {
+    "p_name": "com.newsdog",
+    "a_name": "NewsDog"
+  },
+  {
+    "p_name": "com.quvideo.vivavideo.lite",
+    "a_name": "VivaVideo- QU Video Inc"
+  },
+  {
+    "p_name": "com.lbe.parallel.intl",
+    "a_name": "Parallel Space"
+  },
+  {
+    "p_name": "com.apusapps.browser",
+    "a_name": "APUS Browser"
+  },
+  {
+    "p_name": "com.cyberlink.youperfect",
+    "a_name": "Perfect Corp"
+  },
+  {
+    "p_name": "com.HIsecurity.antivirus.litepro",
+    "a_name": "Virus Cleaner (Hi Security Lab)"
+  },
+  {
+    "p_name": "com.cm.browser.downloader.adblock",
+    "a_name": "CM Browser"
+  },
+  {
+    "p_name": "com.mi.global.bbs",
+    "a_name": "Mi Community"
+  },
+  {
+    "p_name": "com.netqin.ps",
+    "a_name": "Vault-Hide"
+  },
+  {
+    "p_name": "com.cyberlink.youcammakeup",
+    "a_name": "YouCam Makeup"
+  },
+  {
+    "p_name": "com.mi.global.shop",
+    "a_name": "Mi Store"
+  },
+  {
+    "p_name": "com.limsky.ramcleaner",
+    "a_name": "DU Battery Saver"
+  },
+  {
+    "p_name": "com.DU.Cleaner.antivirus.cleanphone",
+    "a_name": "DU Cleaner"
+  },
+  {
+    "p_name": "com.baidu.BaiduMap",
+    "a_name": "Baidu Map"
+  },
+  {
+    "p_name": "com.yubitu.android.YubiCollage",
+    "a_name": "Photo Wonder"
+  },
+  {
+    "p_name": "com.tencent.qqmusic",
+    "a_name": "QQ Music"
+  },
+  {
+    "p_name": "com.tencent.androidqqmail",
+    "a_name": "QQ Mail"
+  },
+  {
+    "p_name": "com.etekcity.vesyncplatform",
+    "a_name": "WeSync"
+  },
+  {
+    "p_name": "com.netease.mail",
+    "a_name": "Mail Master"
+  },
+  {
+    "p_name": "com.miui.videoplayer",
+    "a_name": "Mi Video call-Xiaomi"
+  },
+  {
+    "p_name": "com.pleco.chinesesystem",
+    "a_name": "Pleco"
+  },
+  {
+    "p_name": "com.xinlukou.metroman",
+    "a_name": "China Metro"
+  },
+  {
+    "p_name": "com.snowwhiteapps.downloader",
+    "a_name": "All video downloader"
+  },
+  {
+    "p_name": "com.ss.android.ugc.boomlite",
+    "a_name": "Vigo Lite"
+  },
+  {
+    "p_name": "com.youdao.hindict",
+    "a_name": "Udictionary"
+  },
+  {
+    "p_name": "com.videochat.livu",
+    "a_name": "Livu"
+  },
+  {
+    "p_name": "com.asiainno.uplive",
+    "a_name": "Uplive"
+  },
+  {
+    "p_name": "com.funplus.kingofavalon",
+    "a_name": "Kings of Avalon"
+  },
+  {
+    "p_name": "com.diandian.gog",
+    "a_name": "Guns of glory"
+  },
+  {
+    "p_name": "com.igg.castleclash",
+    "a_name": "Castle Cash"
+  },
+  {
+    "p_name": "com.nono.android",
+    "a_name": "NonoLive"
+  },
+  {
+    "p_name": "com.dating.android",
+    "a_name": "Dating.com"
+  },
+  {
+    "p_name": "com.qidian.Int.reader",
+    "a_name": "Web Novel"
+  },
+  {
+    "p_name": "sg.bigo.hellotalk",
+    "a_name": "Hello Yo"
+  },
+  {
+    "p_name": "com.gtarcade.lod",
+    "a_name": "Lagecy of discord"
+  },
+  {
+    "p_name": "com.longtech.lastwars.gp",
+    "a_name": "Last empire"
+  },
+  {
+    "p_name": "com.lilithgame.sgame.gp.oss",
+    "a_name": "Art of conquest"
+  },
+  {
+    "p_name": "com.flashkeyboardtheme",
+    "a_name": "Flash Keyboard"
+  },
+  {
+    "p_name": "free.vpn.unblock.proxy.turbovpn",
+    "a_name": "Turbo VPN"
+  },
+  {
+    "p_name": "com.quvideo.xiaoying",
+    "a_name": "viva Video"
+  },
+  {
+    "p_name": "com.jetstartgames.chess",
+    "a_name": "Cheez"
+  },
+  {
+    "p_name": "com.alibaba.intl.android.apps.poseidon",
+    "a_name": "Alibaba.com"
+  },
+  {
+    "p_name": "com.zhiliaoapp.musically.go",
+    "a_name": "TikTok Lite"
+  },
+  {
+    "p_name": "com.domobile.applock.lite",
+    "a_name": "AppLock lite"
+  },
+  {
+    "p_name": "com.alibaba.aliexpresshd",
+    "a_name": "AliExpress"
+  },
+  {
+    "p_name": "com.alibaba.aliexpress.itao",
+    "a_name": "iTao"
+  },
+  {
+    "p_name": "com.tool.fileexplorer.filemanager.filetransfer",
+    "a_name": "ES File Manager"
+  },
+  {
+    "p_name": "com.file.manager.filebrowser",
+    "a_name": "File Explorer File Manager"
+  },
+  {
+    "p_name": "com.musicplayer.musica.musicapps.playermusic",
+    "a_name": "Music Player"
+  },
+  {
+    "p_name": "com.mipay.in.wallet",
+    "a_name": "mi pay"
+  },
+  {
+    "p_name": "com.duokan.phone.remotecontroller",
+    "a_name": "mi remote control"
+  },
+  {
+    "p_name": "com.banggood.client",
+    "a_name": "banggood"
+  },
+  {
+    "p_name": "com.micredit.in.gp",
+    "a_name": "Mi Credit"
+  },
+  {
+    "p_name": "cn.wps.moffice_eng",
+    "a_name": "WPS Office"
+  },
+  {
+    "p_name": "cn.wps.moffice_i18n",
+    "a_name": "WPS Office Lite"
+  },
+  {
+    "p_name": "cn.wps.pdf",
+    "a_name": "WPS PDF"
+  },
+  {
+    "p_name": "cn.wps.moffice_extra",
+    "a_name": "WPS Office Extra"
+  },
+  {
+    "p_name": "cn.wps.moffice_premium",
+    "a_name": "WPS Premium Subscription"
+  },
+  {
+    "p_name": "cn.wps.pdf.fillsign",
+    "a_name": "WPS PDF Fill"
+  },
+  {
+    "p_name": "om.dewmobile.kuaiya.play",
+    "a_name": "Zapya - File Transfer, Sharing Music Playlist"
+  },
+  {
+    "p_name": "com.dewmobile.kuaiya.web",
+    "a_name": "Zapya WebShare - File Sharing in Web Browser"
+  },
+  {
+    "p_name": "com.dewmobile.zapyago",
+    "a_name": "Zapya Go - From File Transfer to Private Social"
+  },
+  {
+    "p_name": "com.dewmobile.kuaiya.recorder",
+    "a_name": "RecorderZ - Screen Recorder by Zapya"
+  },
+  {
+    "p_name": "com.dewmobile.kuaiya.paintpad.play",
+    "a_name": "Zapya Doodle"
+  },
+  {
+    "p_name": "com.dewmobile.kuaiya.game.airhockey.play",
+    "a_name": "Zapya Air Hockey"
+  },
+  {
+    "p_name": "com.dewmobile.kuaiya.zproj.screenlockz",
+    "a_name": "ScreenLockZ by Zapya"
+  },
+  {
+    "p_name": "com.dewmobile.game",
+    "a_name": "Zapya MiniGame"
+  },
+  {
+    "p_name": "com.dewmobile.kuaibao.gp",
+    "a_name": "MyDearest"
+  },
+  {
+    "p_name": "com.dewmobile.kuaiya.zproj.applockz",
+    "a_name": "AppLockZ by Zapya"
+  },
+  {
+    "p_name": "com.dewmobile.kuaiya.filez",
+    "a_name": "FileZ - Easy File Manager"
+  },
+  {
+    "p_name": "com.alibaba.android.rimet",
+    "a_name": "DingTalk"
+  },
+  {
+    "p_name": "com.alibaba.dingtalk.global",
+    "a_name": "DingTalk Lite"
+  }
+]

BIN
app/src/main/assets/rca.db


+ 4 - 4
app/src/main/java/com/chinaappsremover/AppController.java

@@ -8,17 +8,17 @@ import com.chinaappsremover.dbhandler.DataBaseHelper;
 
 public class AppController extends Application {
     private static DataBaseHelper dataBaseHelper;
-    private static AppController instace;
+    private static AppController instance;
     private final String DEFAULT_PREF = "default_pref";
     private SharedPreferences default_prefs;
 
-    public static AppController getInstace() {
-        return instace;
+    public static AppController getInstance() {
+        return instance;
     }
 
     public void onCreate() {
         super.onCreate();
-        instace = this;
+        instance = this;
         default_prefs = getSharedPreferences(DEFAULT_PREF, Context.MODE_PRIVATE);
         dataBaseHelper = new DataBaseHelper(this);
     }

+ 9 - 0
app/src/main/java/com/chinaappsremover/dbhandler/AppInfoEntry.java

@@ -0,0 +1,9 @@
+package com.chinaappsremover.dbhandler;
+
+import android.provider.BaseColumns;
+
+class AppInfoEntry implements BaseColumns {
+    public static final String TABLE_NAME = "apps";
+    public static final String COLUMN_PACKAGE_NAME = "p_name";
+    public static final String COLUMN_APP_NAME = "a_name";
+}

+ 58 - 27
app/src/main/java/com/chinaappsremover/dbhandler/DataBaseHelper.java

@@ -1,5 +1,6 @@
 package com.chinaappsremover.dbhandler;
 
+import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
@@ -14,51 +15,81 @@ import java.util.List;
 public class DataBaseHelper extends SQLiteOpenHelper {
     public static final String DATABASE_NAME = "rca.db";
     public static final int DATABASE_VERSION = 1;
-    private Context myContext;
-    private SQLiteDatabase sqLiteDatabase = getWritableDatabase();
+
+    private static final String SQL_CREATE_ENTRIES =
+            "CREATE TABLE " + AppInfoEntry.TABLE_NAME + " (" +
+                    AppInfoEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
+                    AppInfoEntry.COLUMN_PACKAGE_NAME + " TEXT," +
+                    AppInfoEntry.COLUMN_APP_NAME + " TEXT)";
+
+    private static final String SQL_DELETE_ENTRIES =
+            "DROP TABLE IF EXISTS " + AppInfoEntry.TABLE_NAME;
+
 
     public DataBaseHelper(Context context) {
         super(context, DATABASE_NAME, null, DATABASE_VERSION);
-        myContext = context;
     }
 
-    public void onCreate(SQLiteDatabase sQLiteDatabase) {
-        Log.w("Db oncrate called", "Db oncreate called");
-        sQLiteDatabase.execSQL("create table apps (id integer primary key autoincrement, p_name text, a_name text);");
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+        Log.w("Db oncrate called", "Db oncreate called" + SQL_CREATE_ENTRIES);
+        db.execSQL(SQL_CREATE_ENTRIES);
     }
 
-    public void onUpgrade(SQLiteDatabase sQLiteDatabase, int i, int i2) {
-        myContext.getDatabasePath("dest.sqLiteDatabase");
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+        if (newVersion != oldVersion) {
+            db.execSQL(SQL_DELETE_ENTRIES);
+            onCreate(db);
+        }
     }
 
-    public boolean attach(File file, boolean z) {
-        try {
-            sqLiteDatabase.execSQL("attach database ? as sqLiteDatabase", new String[]{file.getAbsolutePath()});
-            if (!z) {
-                sqLiteDatabase.delete("apps", null, null);
-            }
-            sqLiteDatabase.execSQL("INSERT INTO apps (p_name, a_name) SELECT  p_name, a_name FROM sqLiteDatabase.apps");
-            return file.delete();
-        } catch (Exception e) {
-            e.printStackTrace();
-            return false;
-        }
+    @Override
+    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+        onUpgrade(db, oldVersion, newVersion);
     }
 
-    public List<AppInfo> isExist(List<AppInfo> list, List<AppInfo> list2) {
-        list2.clear();
-        for (AppInfo next : list) {
-            Cursor rawQuery = sqLiteDatabase.rawQuery("select * from  apps where p_name ='" + next.packageName + "'", null);
+
+    public List<AppInfo> isExist(List<AppInfo> allInstalledAppInfoList, List<AppInfo> chinaAppInfoList) {
+        SQLiteDatabase db = getWritableDatabase();
+        chinaAppInfoList.clear();
+        for (AppInfo next : allInstalledAppInfoList) {
+            Cursor rawQuery = db.rawQuery("select * from  apps where p_name ='" + next.packageName + "'", null);
             if (rawQuery != null && rawQuery.moveToFirst()) {
-                list2.add(next);
+                chinaAppInfoList.add(next);
                 while (rawQuery.moveToNext()) {
-                    list2.add(next);
+                    chinaAppInfoList.add(next);
                 }
             }
             if (rawQuery != null) {
                 rawQuery.close();
             }
         }
-        return list2;
+        return chinaAppInfoList;
+    }
+
+    public boolean refreshAppInfos(List<AppInfo> appInfoEntries) {
+        SQLiteDatabase db = getWritableDatabase();
+        db.beginTransaction();
+        try {
+            db.delete(AppInfoEntry.TABLE_NAME, null, null);
+
+            for (AppInfo appInfo : appInfoEntries) {
+
+                ContentValues values = new ContentValues();
+                values.put(AppInfoEntry.COLUMN_APP_NAME, appInfo.appName
+                );
+                values.put(AppInfoEntry.COLUMN_PACKAGE_NAME, appInfo.packageName);
+
+                db.insertOrThrow(AppInfoEntry.TABLE_NAME, null, values);
+            }
+            db.setTransactionSuccessful();
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            db.endTransaction();
+        }
+        return false;
     }
 }

+ 0 - 55
app/src/main/java/com/chinaappsremover/dbhandler/DbUtils.java

@@ -1,55 +0,0 @@
-package com.chinaappsremover.dbhandler;
-
-import android.content.Context;
-import android.util.Log;
-
-import com.chinaappsremover.AppController;
-import com.chinaappsremover.utils.Preference;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-public class DbUtils {
-    public File getDatabasePath(Context context, String str) {
-        return context.getDatabasePath(str);
-    }
-
-    public static void attachDB(Context context) {
-        File databasePath = context.getDatabasePath("dest.sqLiteDatabase");
-        if (getDbFile(context, DataBaseHelper.DATABASE_NAME, databasePath.getAbsolutePath()) && AppController.getDbHelper().attach(databasePath, false)) {
-            Preference.setAttachedDb();
-        }
-    }
-
-    public static boolean getDbFile(Context context, String str, String str2) {
-        try {
-            InputStream open = context.getAssets().open(str);
-            FileOutputStream fileOutputStream = new FileOutputStream(str2);
-            Log.v("Tag assets", fileOutputStream.toString());
-            byte[] bArr = new byte[1024];
-            while (true) {
-                int read = open.read(bArr);
-                if (read > 0) {
-                    fileOutputStream.write(bArr, 0, read);
-                } else {
-                    open.close();
-                    fileOutputStream.flush();
-                    fileOutputStream.close();
-                    return true;
-                }
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-            return false;
-        }
-    }
-
-    public boolean isDatabaseAttached(Context context, String str) {
-        if (getDatabasePath(context, str) == null) {
-            return false;
-        }
-        return Preference.isDBAttached();
-    }
-}

+ 80 - 0
app/src/main/java/com/chinaappsremover/network/ChinaAppsDataDownloader.java

@@ -0,0 +1,80 @@
+package com.chinaappsremover.network;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.chinaappsremover.BuildConfig;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import okhttp3.Cache;
+import okhttp3.CacheControl;
+import okhttp3.OkHttpClient;
+import okhttp3.Protocol;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+
+public class ChinaAppsDataDownloader {
+    private static volatile ChinaAppsDataDownloader sInstance = null;
+
+    private final OkHttpClient okHttpClient;
+
+    public static ChinaAppsDataDownloader getInstance(Context applicationContext) {
+        if (sInstance == null) {
+            synchronized (ChinaAppsDataDownloader.class) {
+                if (sInstance == null) {
+                    sInstance = new ChinaAppsDataDownloader(applicationContext);
+                }
+            }
+        }
+        return sInstance;
+    }
+
+    private ChinaAppsDataDownloader(Context context) {
+
+        ArrayList<Protocol> protocols = new ArrayList<>();
+        protocols.add(Protocol.HTTP_1_1);
+        protocols.add(Protocol.HTTP_2);
+
+        long cacheSize = 2L * 1024 * 1024; // 2 MiB
+        File cacheDir = context.getDir("china_apps_data", Context.MODE_PRIVATE);
+        Cache cache = new Cache(cacheDir, cacheSize);
+
+        okHttpClient = new OkHttpClient.Builder()
+                .protocols(protocols)
+                .cache(cache)
+                .build();
+
+        if (sInstance != null) {
+            throw new AssertionError(
+                    "Another instance of "
+                            + ChinaAppsDataDownloader.class.getName()
+                            + " class already exists, Can't create a new instance.");
+        }
+    }
+
+    public Response fetch(boolean fromCache) throws IOException {
+        String url = BuildConfig.BLACKLISTED_APP_JSON_URL;
+        Request request = new Request.Builder()
+                .url(url)
+                .cacheControl(fromCache ? CacheControl.FORCE_CACHE : CacheControl.FORCE_NETWORK)
+                .build();
+        try {
+            Response response = okHttpClient.newCall(request).execute();
+            ResponseBody responseBody = response.body();
+            if (responseBody != null) {
+                Log.d("ChinaAppsDataDownloader", fromCache ? "Loaded cache. bytes: " + responseBody.contentLength() : "Fetched bytes: " + responseBody.contentLength());
+            }
+            if (response.code() == 504) {
+                return null;
+            }
+            return response;
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new IOException("Network error");
+        }
+    }
+}

+ 81 - 0
app/src/main/java/com/chinaappsremover/network/ChinaAppsDataParser.java

@@ -0,0 +1,81 @@
+package com.chinaappsremover.network;
+
+import com.chinaappsremover.wrapper.AppInfo;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+
+public class ChinaAppsDataParser {
+    private static final String KEY_PACKAGE_NAME = "p_name";
+    private static final String KEY_APP_NAME = "a_name";
+
+    private ChinaAppsDataParser() {
+    }
+
+    public static List<AppInfo> parse(Response response) {
+        ResponseBody responseBody = response.body();
+        if (responseBody != null) {
+            try {
+                return parse(responseBody.string());
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    @SuppressWarnings("CharsetObjectCanBeUsed")
+    public static List<AppInfo> parse(InputStream inputStream) {
+        if (inputStream != null) {
+            try {
+
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
+                StringBuilder builder = new StringBuilder();
+
+                String line;
+                while ((line = bufferedReader.readLine()) != null) {
+                    builder.append(line);
+                }
+                return parse(builder.toString());
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    public static List<AppInfo> parse(String json) {
+        if (json != null) {
+            try {
+                List<AppInfo> appInfoList = new ArrayList<>();
+
+                JSONArray jsonArray = new JSONArray(json);
+                for (int i = 0; i < jsonArray.length(); i++) {
+                    JSONObject jsonObject = jsonArray.getJSONObject(i);
+
+                    AppInfo appInfo = new AppInfo();
+                    appInfo.appName = jsonObject.getString(KEY_APP_NAME);
+                    appInfo.packageName = jsonObject.getString(KEY_PACKAGE_NAME);
+                    appInfoList.add(appInfo);
+                }
+
+                return appInfoList;
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+}

+ 81 - 2
app/src/main/java/com/chinaappsremover/ui/MainActivity.java

@@ -33,7 +33,12 @@ import androidx.recyclerview.widget.RecyclerView;
 import com.chinaappsremover.AppController;
 import com.chinaappsremover.R;
 import com.chinaappsremover.adapter.ItemlistAdapter;
+import com.chinaappsremover.dbhandler.DataBaseHelper;
 import com.chinaappsremover.listener.OnItemClickListener;
+import com.chinaappsremover.network.ChinaAppsDataDownloader;
+import com.chinaappsremover.network.ChinaAppsDataParser;
+import com.chinaappsremover.utils.NetworkUtils;
+import com.chinaappsremover.utils.Preference;
 import com.chinaappsremover.wrapper.AppInfo;
 import com.google.android.material.snackbar.Snackbar;
 import com.google.android.play.core.appupdate.AppUpdateInfo;
@@ -47,11 +52,15 @@ import com.google.android.play.core.install.model.UpdateAvailability;
 import com.google.android.play.core.tasks.OnSuccessListener;
 
 import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
+import okhttp3.Response;
+
 public class MainActivity extends AppCompatActivity implements OnItemClickListener {
 
     private static final int RC_APP_UPDATE = 201;
@@ -219,7 +228,19 @@ public class MainActivity extends AppCompatActivity implements OnItemClickListen
         }
     }
 
-    class GetAppsAsync extends AsyncTask<Void, Void, List<AppInfo>> {
+    enum NetworkRefreshState {
+        ONGOING,
+        COMPLETED,
+        FAILED
+    }
+
+    class GetAppsAsync extends AsyncTask<Void, NetworkRefreshState, List<AppInfo>> {
+
+        private Snackbar snackbar;
+
+        GetAppsAsync() {
+            snackbar = Snackbar.make(findViewById(R.id.scan_ui), "Refreshing database from the network...", Snackbar.LENGTH_INDEFINITE);
+        }
 
         protected void onPreExecute() {
             super.onPreExecute();
@@ -228,7 +249,65 @@ public class MainActivity extends AppCompatActivity implements OnItemClickListen
         }
 
         protected List<AppInfo> doInBackground(Void... voidArr) {
-            return AppController.getDbHelper().isExist(getInstalledApps(), appInfos);
+            DataBaseHelper dbHelper = AppController.getDbHelper();
+            if (NetworkUtils.hasNetworkConnection() && Preference.shouldRefreshData()) {
+                Log.d("MainActivity", "Refreshing database from the network...");
+                publishProgress(NetworkRefreshState.ONGOING);
+                ChinaAppsDataDownloader dataDownloader = ChinaAppsDataDownloader.getInstance(getApplicationContext());
+                try {
+                    Thread.sleep(5000L);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                try {
+                    Response response = dataDownloader.fetch(false);
+                    List<AppInfo> appInfoList = ChinaAppsDataParser.parse(response);
+                    if (appInfoList != null && dbHelper.refreshAppInfos(appInfoList)) {
+                        Log.d("MainActivity", "Refreshed database from the network.");
+                        Preference.updateLastSyncMills();
+                        Preference.setDbInitialized();
+                        publishProgress(NetworkRefreshState.COMPLETED);
+                    } else {
+                        publishProgress(NetworkRefreshState.FAILED);
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            } else {
+                if (Preference.isDbInitialized()) {
+                    Log.d("MainActivity", "DB not initialized, initializing database from local json...");
+                    try {
+                        InputStream stream = getApplicationContext().getAssets().open("china_apps.json");
+                        List<AppInfo> appInfoList = ChinaAppsDataParser.parse(stream);
+                        if (appInfoList != null && dbHelper.refreshAppInfos(appInfoList)) {
+                            Preference.setDbInitialized();
+                            Log.d("MainActivity", "Initialized database from local json.");
+                        }
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+            return dbHelper.isExist(getInstalledApps(), appInfos);
+        }
+
+
+        @Override
+        protected void onProgressUpdate(NetworkRefreshState... values) {
+            super.onProgressUpdate(values);
+            if (values != null && values.length > 0) {
+                NetworkRefreshState networkRefreshState = values[0];
+                if (networkRefreshState == NetworkRefreshState.ONGOING) {
+                    snackbar.show();
+                } else {
+                    snackbar.dismiss();
+                    if (networkRefreshState == NetworkRefreshState.COMPLETED) {
+                        Snackbar.make(findViewById(R.id.scan_ui), "Database refreshed successfully.", Snackbar.LENGTH_SHORT).show();
+                    }else {
+                        Snackbar.make(findViewById(R.id.scan_ui), "Aw, Snap! Something went wrong while refreshing.", Snackbar.LENGTH_SHORT).show();
+                    }
+                }
+            }
         }
 
         protected void onPostExecute(List<AppInfo> list) {

+ 0 - 2
app/src/main/java/com/chinaappsremover/ui/Splash.java

@@ -8,7 +8,6 @@ import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.app.AppCompatActivity;
 
 import com.chinaappsremover.R;
-import com.chinaappsremover.dbhandler.DbUtils;
 
 public class Splash extends AppCompatActivity {
 
@@ -21,7 +20,6 @@ public class Splash extends AppCompatActivity {
             supportActionBar.hide();
         }
 
-        DbUtils.attachDB(this);
         new Handler().postDelayed(new Runnable() {
             public void run() {
                 Splash splash = Splash.this;

+ 19 - 0
app/src/main/java/com/chinaappsremover/utils/NetworkUtils.java

@@ -0,0 +1,19 @@
+package com.chinaappsremover.utils;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+
+import com.chinaappsremover.AppController;
+
+public class NetworkUtils {
+    private NetworkUtils() {
+    }
+
+    public static boolean hasNetworkConnection() {
+        Context context = AppController.getInstance().getApplicationContext();
+        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
+        return activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting();
+    }
+}

+ 18 - 5
app/src/main/java/com/chinaappsremover/utils/Preference.java

@@ -2,14 +2,27 @@ package com.chinaappsremover.utils;
 
 import com.chinaappsremover.AppController;
 
+import static com.chinaappsremover.AppController.getInstance;
+
 public class Preference {
-    private static final String isDBAttached = "isDBAttached";
+    private static final Long MIN_SYNC_DURATION = 1000L * 60 * 60 * 12; // 12Hours
+    private static final String KEY_LAST_SYNC_MILLS = "last_sync_mills";
+    private static final String KEY_FIRST_RUN = "db_initialized";
+
+    public static boolean shouldRefreshData() {
+        Long lastSyncedMills = getInstance().getDefaultPreference().getLong(KEY_LAST_SYNC_MILLS, 0L);
+        return System.currentTimeMillis() >= lastSyncedMills + MIN_SYNC_DURATION;
+    }
+
+    public static void updateLastSyncMills() {
+        getInstance().getDefaultPreference().edit().putLong(KEY_LAST_SYNC_MILLS, System.currentTimeMillis()).apply();
+    }
 
-    public static boolean isDBAttached() {
-        return AppController.getInstace().getDefaultPreference().getBoolean(isDBAttached, false);
+    public static boolean isDbInitialized() {
+        return AppController.getInstance().getDefaultPreference().getBoolean(KEY_FIRST_RUN, true);
     }
 
-    public static void setAttachedDb() {
-        AppController.getInstace().getDefaultPreference().edit().putBoolean(isDBAttached, false).apply();
+    public static void setDbInitialized() {
+        AppController.getInstance().getDefaultPreference().edit().putBoolean(KEY_FIRST_RUN, false).apply();
     }
 }

+ 1 - 1
app/src/main/java/com/chinaappsremover/utils/uihelper/CustomFontButton.java

@@ -28,7 +28,7 @@ public class CustomFontButton extends AppCompatButton {
             String fontName = a.getString(R.styleable.CustomFontButton_custom_font_btn);
             if (fontName != null) {
                 try {
-                    Typeface myTypeface = Typeface.createFromAsset(getContext().getAssets(), "fonts/" + fontName + ".ttf");
+                    Typeface myTypeface = Typeface.createFromAsset(getContext().getAssets(), "font/" + fontName + ".ttf");
                     setTypeface(myTypeface);
                 } catch (Exception e) {
                     Log.e("failed", e.getMessage());

+ 1 - 1
app/src/main/java/com/chinaappsremover/utils/uihelper/CustomFontTextView.java

@@ -28,7 +28,7 @@ public class CustomFontTextView extends AppCompatTextView {
             String fontName = a.getString(R.styleable.CustomFontTextView_custom_font);
             if (fontName != null) {
                 try {
-                    Typeface myTypeface = Typeface.createFromAsset(getContext().getAssets(), "fonts/" + fontName + ".ttf");
+                    Typeface myTypeface = Typeface.createFromAsset(getContext().getAssets(), "font/" + fontName + ".ttf");
                     setTypeface(myTypeface);
                 } catch (Exception e) {
                     Log.e("failed", e.getMessage());

BIN
app_logo.png