mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2026-01-25 04:54:07 +01:00
android support! thanks to TachiWeb devs.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
package xyz.nulldev.androidcompat
|
||||
|
||||
import android.app.Application
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import xyz.nulldev.androidcompat.androidimpl.CustomContext
|
||||
|
||||
class AndroidCompat {
|
||||
|
||||
val context: CustomContext by DI.global.instance()
|
||||
|
||||
fun startApp(application: Application) {
|
||||
application.attach(context)
|
||||
application.onCreate()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package xyz.nulldev.androidcompat
|
||||
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import xyz.nulldev.androidcompat.bytecode.ModApplier
|
||||
import xyz.nulldev.androidcompat.config.ApplicationInfoConfigModule
|
||||
import xyz.nulldev.androidcompat.config.FilesConfigModule
|
||||
import xyz.nulldev.androidcompat.config.SystemConfigModule
|
||||
import xyz.nulldev.ts.config.GlobalConfigManager
|
||||
|
||||
/**
|
||||
* Initializes the Android compatibility module
|
||||
*/
|
||||
class AndroidCompatInitializer {
|
||||
|
||||
val modApplier by lazy { ModApplier() }
|
||||
|
||||
fun init() {
|
||||
modApplier.apply()
|
||||
|
||||
DI.global.addImport(AndroidCompatModule().create())
|
||||
|
||||
//Register config modules
|
||||
GlobalConfigManager.registerModules(
|
||||
FilesConfigModule.register(GlobalConfigManager.config),
|
||||
ApplicationInfoConfigModule.register(GlobalConfigManager.config),
|
||||
SystemConfigModule.register(GlobalConfigManager.config)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package xyz.nulldev.androidcompat
|
||||
|
||||
import android.content.Context
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.bind
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import org.kodein.di.singleton
|
||||
import xyz.nulldev.androidcompat.androidimpl.CustomContext
|
||||
import xyz.nulldev.androidcompat.androidimpl.FakePackageManager
|
||||
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl
|
||||
import xyz.nulldev.androidcompat.io.AndroidFiles
|
||||
import xyz.nulldev.androidcompat.pm.PackageController
|
||||
import xyz.nulldev.androidcompat.service.ServiceSupport
|
||||
|
||||
/**
|
||||
* AndroidCompatModule
|
||||
*/
|
||||
|
||||
class AndroidCompatModule {
|
||||
fun create() = DI.Module("AndroidCompat") {
|
||||
bind<AndroidFiles>() with singleton { AndroidFiles() }
|
||||
|
||||
bind<ApplicationInfoImpl>() with singleton { ApplicationInfoImpl() }
|
||||
|
||||
bind<ServiceSupport>() with singleton { ServiceSupport() }
|
||||
|
||||
bind<FakePackageManager>() with singleton { FakePackageManager() }
|
||||
|
||||
bind<PackageController>() with singleton { PackageController() }
|
||||
|
||||
//Context
|
||||
bind<CustomContext>() with singleton { CustomContext() }
|
||||
bind<Context>() with singleton {
|
||||
val context: Context by DI.global.instance<CustomContext>()
|
||||
context
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,738 @@
|
||||
/*
|
||||
* Copyright 2016 Andy Bao
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package xyz.nulldev.androidcompat.androidimpl;
|
||||
|
||||
import android.content.*;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.database.DatabaseErrorHandler;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Uri;
|
||||
import android.os.*;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayAdjustments;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.kodein.di.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl;
|
||||
import xyz.nulldev.androidcompat.io.AndroidFiles;
|
||||
import xyz.nulldev.androidcompat.io.sharedprefs.JsonSharedPreferences;
|
||||
import xyz.nulldev.androidcompat.service.ServiceSupport;
|
||||
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Custom context implementation.
|
||||
*
|
||||
* TODO Deal with packagemanager for extension sources
|
||||
*/
|
||||
public class CustomContext extends Context implements DIAware {
|
||||
private DI kodein;
|
||||
public CustomContext() {
|
||||
this(KodeinGlobalHelper.kodein());
|
||||
}
|
||||
|
||||
public CustomContext(DI kodein) {
|
||||
this.kodein = kodein;
|
||||
|
||||
//Init configs
|
||||
androidFiles = KodeinGlobalHelper.instance(AndroidFiles.class, getDi());
|
||||
applicationInfo = KodeinGlobalHelper.instance(ApplicationInfoImpl.class, getDi());
|
||||
serviceSupport = KodeinGlobalHelper.instance(ServiceSupport.class, getDi());
|
||||
fakePackageManager = KodeinGlobalHelper.instance(FakePackageManager.class, getDi());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DI getDi() {
|
||||
return kodein;
|
||||
}
|
||||
|
||||
private AndroidFiles androidFiles;
|
||||
private ApplicationInfoImpl applicationInfo;
|
||||
private ServiceSupport serviceSupport;
|
||||
private FakePackageManager fakePackageManager;
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(CustomContext.class);
|
||||
|
||||
private Map<String, Object> serviceMap = new HashMap<>();
|
||||
{
|
||||
serviceMap.put(Context.CONNECTIVITY_SERVICE, ConnectivityManager.INSTANCE);
|
||||
serviceMap.put(Context.POWER_SERVICE, PowerManager.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetManager getAssets() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageManager getPackageManager() {
|
||||
return fakePackageManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentResolver getContentResolver() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Looper getMainLooper() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getApplicationContext() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheme(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources.Theme getTheme() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return this.getClass().getClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPackageName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBasePackageName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOpPackageName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationInfo getApplicationInfo() {
|
||||
return applicationInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPackageResourcePath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPackageCodePath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Fake shared prefs! **/
|
||||
private Map<String, SharedPreferences> prefs = new HashMap<>(); //Cache
|
||||
|
||||
private File sharedPrefsFileFromString(String s) {
|
||||
return new File(androidFiles.getPrefsDir(), s + ".json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized SharedPreferences getSharedPreferences(String s, int i) {
|
||||
SharedPreferences preferences = prefs.get(s);
|
||||
//Create new shared preferences if one does not exist
|
||||
if(preferences == null) {
|
||||
preferences = getSharedPreferences(sharedPrefsFileFromString(s), i);
|
||||
prefs.put(s, preferences);
|
||||
}
|
||||
return preferences;
|
||||
}
|
||||
|
||||
public SharedPreferences getSharedPreferences(File file, int mode) {
|
||||
return new JsonSharedPreferences(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSharedPreferences(String name) {
|
||||
prefs.remove(name);
|
||||
return sharedPrefsFileFromString(name).delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileInputStream openFileInput(String s) throws FileNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileOutputStream openFileOutput(String s, int i) throws FileNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteFile(String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFileStreamPath(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getSharedPreferencesPath(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataDir() {
|
||||
return androidFiles.getDataDir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFilesDir() {
|
||||
return androidFiles.getFilesDir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getNoBackupFilesDir() {
|
||||
return androidFiles.getNoBackupFilesDir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getExternalFilesDir(String s) {
|
||||
return androidFiles.getExternalFilesDirs().get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] getExternalFilesDirs(String s) {
|
||||
List<File> files = androidFiles.getExternalFilesDirs();
|
||||
return files.toArray(new File[files.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getObbDir() {
|
||||
return androidFiles.getObbDirs().get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] getObbDirs() {
|
||||
List<File> files = androidFiles.getObbDirs();
|
||||
return files.toArray(new File[files.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getCacheDir() {
|
||||
return androidFiles.getCacheDir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getCodeCacheDir() {
|
||||
return androidFiles.getCodeCacheDir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getExternalCacheDir() {
|
||||
return androidFiles.getExternalCacheDirs().get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] getExternalCacheDirs() {
|
||||
List<File> files = androidFiles.getExternalCacheDirs();
|
||||
return files.toArray(new File[files.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] getExternalMediaDirs() {
|
||||
List<File> files = androidFiles.getExternalMediaDirs();
|
||||
return files.toArray(new File[files.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] fileList() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDir(String s, int i) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLiteDatabase openOrCreateDatabase(String s, int i, SQLiteDatabase.CursorFactory cursorFactory) {
|
||||
return openOrCreateDatabase(s, i, cursorFactory, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLiteDatabase openOrCreateDatabase(String s, int i, SQLiteDatabase.CursorFactory cursorFactory, DatabaseErrorHandler databaseErrorHandler) {
|
||||
return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(s).getAbsolutePath(), cursorFactory, databaseErrorHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveDatabaseFrom(Context sourceContext, String name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteDatabase(String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDatabasePath(String s) {
|
||||
return new File(new File(androidFiles.getRootDir(), "databases"), s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] databaseList() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getWallpaper() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable peekWallpaper() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWallpaperDesiredMinimumWidth() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWallpaperDesiredMinimumHeight() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWallpaper(Bitmap bitmap) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWallpaper(InputStream inputStream) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearWallpaper() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivity(Intent intent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivity(Intent intent, Bundle bundle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivities(Intent[] intents) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivities(Intent[] intents, Bundle bundle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startIntentSender(IntentSender intentSender, Intent intent, int i, int i1, int i2) throws IntentSender.SendIntentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startIntentSender(IntentSender intentSender, Intent intent, int i, int i1, int i2, Bundle bundle) throws IntentSender.SendIntentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBroadcast(Intent intent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBroadcast(Intent intent, String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendOrderedBroadcast(Intent intent, String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendOrderedBroadcast(Intent intent, String s, BroadcastReceiver broadcastReceiver, Handler handler, int i, String s1, Bundle bundle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendOrderedBroadcast(Intent intent, String receiverPermission, Bundle options, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBroadcastAsUser(Intent intent, UserHandle userHandle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBroadcastAsUser(Intent intent, UserHandle userHandle, String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, int appOp) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendOrderedBroadcastAsUser(Intent intent, UserHandle userHandle, String s, BroadcastReceiver broadcastReceiver, Handler handler, int i, String s1, Bundle bundle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, int appOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendStickyBroadcast(Intent intent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver broadcastReceiver, Handler handler, int i, String s, Bundle bundle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeStickyBroadcast(Intent intent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendStickyBroadcastAsUser(Intent intent, UserHandle userHandle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle userHandle, BroadcastReceiver broadcastReceiver, Handler handler, int i, String s, Bundle bundle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeStickyBroadcastAsUser(Intent intent, UserHandle userHandle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent registerReceiver(BroadcastReceiver broadcastReceiver, IntentFilter intentFilter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent registerReceiver(BroadcastReceiver broadcastReceiver, IntentFilter intentFilter, String s, Handler handler) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, IntentFilter filter, String broadcastPermission, Handler scheduler) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void unregisterReceiver(BroadcastReceiver broadcastReceiver) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentName startService(Intent intent) {
|
||||
serviceSupport.startService(this, intent);
|
||||
return intent.getComponent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopService(Intent intent) {
|
||||
serviceSupport.stopService(this, intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentName startServiceAsUser(Intent service, UserHandle user) {
|
||||
logger.warn("An attempt was made to start the service: '{}' as another user! Since multiple user services are currently not supported, the service will be started as the current user!", service.getComponent().getClassName());
|
||||
return startService(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopServiceAsUser(Intent service, UserHandle user) {
|
||||
logger.warn("An attempt was made to stop the service: '{}' as another user! Since multiple user services are currently not supported, the service will be stopped as the current user!", service.getComponent().getClassName());
|
||||
return stopService(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bindService(Intent intent, ServiceConnection serviceConnection, int i) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindService(ServiceConnection serviceConnection) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInstrumentation(ComponentName componentName, String s, Bundle bundle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSystemService(String s) {
|
||||
return serviceMap.get(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSystemServiceName(Class<?> aClass) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkPermission(String s, int i, int i1) {
|
||||
return PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
|
||||
return PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkCallingPermission(String s) {
|
||||
return PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkCallingOrSelfPermission(String s) {
|
||||
return PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkSelfPermission(String s) {
|
||||
return PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enforcePermission(String s, int i, int i1, String s1) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enforceCallingPermission(String s, String s1) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enforceCallingOrSelfPermission(String s, String s1) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantUriPermission(String s, Uri uri, int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeUriPermission(Uri uri, int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkUriPermission(Uri uri, int i, int i1, int i2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkCallingUriPermission(Uri uri, int i) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkCallingOrSelfUriPermission(Uri uri, int i) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkUriPermission(Uri uri, String s, String s1, int i, int i1, int i2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enforceUriPermission(Uri uri, int i, int i1, int i2, String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enforceCallingUriPermission(Uri uri, int i, String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enforceCallingOrSelfUriPermission(Uri uri, int i, String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enforceUriPermission(Uri uri, String s, String s1, int i, int i1, int i2, String s2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context createPackageContext(String s, int i) throws PackageManager.NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) throws PackageManager.NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context createApplicationContext(ApplicationInfo application, int flags) throws PackageManager.NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUserId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context createConfigurationContext(Configuration configuration) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context createDisplayContext(Display display) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context createDeviceProtectedStorageContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context createCredentialProtectedStorageContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DisplayAdjustments getDisplayAdjustments(int displayId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Display getDisplay() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeviceProtectedStorage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialProtectedStorage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DIContext<?> getDiContext() {
|
||||
return getDi().getDiContext();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DITrigger getDiTrigger() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,849 @@
|
||||
package xyz.nulldev.androidcompat.androidimpl;
|
||||
|
||||
import android.app.PackageInstallObserver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.*;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import kotlin.NotImplementedError;
|
||||
import xyz.nulldev.androidcompat.pm.InstalledPackage;
|
||||
import xyz.nulldev.androidcompat.pm.PackageController;
|
||||
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FakePackageManager extends PackageManager {
|
||||
private PackageController controller = KodeinGlobalHelper.instance(PackageController.class);
|
||||
|
||||
@Override
|
||||
public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException {
|
||||
InstalledPackage installedPackage = controller.findPackage(packageName);
|
||||
|
||||
if(installedPackage == null) throw new NameNotFoundException();
|
||||
|
||||
return installedPackage.getInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageInfo getPackageInfo(VersionedPackage versionedPackage, int flags) throws NameNotFoundException {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) throws NameNotFoundException {
|
||||
return getPackageInfo(packageName, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] currentToCanonicalPackageNames(String[] names) {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] canonicalToCurrentPackageNames(String[] names) {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getLaunchIntentForPackage(String packageName) {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getLeanbackLaunchIntentForPackage(String packageName) {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getPackageGids(String packageName) throws NameNotFoundException {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getPackageGids(String packageName, int flags) throws NameNotFoundException {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPackageUid(String packageName, int flags) throws NameNotFoundException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPackageUidAsUser(String packageName, int userId) throws NameNotFoundException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPackageUidAsUser(String packageName, int flags, int userId) throws NameNotFoundException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionInfo getPermissionInfo(String name, int flags) throws NameNotFoundException {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) throws NameNotFoundException {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionReviewModeEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) throws NameNotFoundException {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationInfo getApplicationInfo(String packageName, int flags) throws NameNotFoundException {
|
||||
return getPackageInfo(packageName, flags).applicationInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationInfo getApplicationInfoAsUser(String packageName, int flags, int userId) throws NameNotFoundException {
|
||||
return getPackageInfoAsUser(packageName, flags, userId).applicationInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivityInfo getActivityInfo(ComponentName component, int flags) throws NameNotFoundException {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivityInfo getReceiverInfo(ComponentName component, int flags) throws NameNotFoundException {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceInfo getServiceInfo(ComponentName component, int flags) throws NameNotFoundException {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderInfo getProviderInfo(ComponentName component, int flags) throws NameNotFoundException {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
//TODO Return loaded extensions
|
||||
@Override
|
||||
public List<PackageInfo> getInstalledPackages(int flags) {
|
||||
return controller.listInstalled().stream().map(InstalledPackage::getInfo).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PackageInfo> getPackagesHoldingPermissions(String[] permissions, int flags) {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkPermission(String permName, String pkgName) {
|
||||
return PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionRevokedByPolicy(String permName, String pkgName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionControllerPackageName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addPermission(PermissionInfo info) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addPermissionAsync(PermissionInfo info) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePermission(String name) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantRuntimePermission(String packageName, String permissionName, UserHandle user) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeRuntimePermission(String packageName, String permissionName, UserHandle user) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPermissionFlags(String permissionName, String packageName, UserHandle user) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePermissionFlags(String permissionName, String packageName, int flagMask, int flagValues, UserHandle user) {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowRequestPermissionRationale(String permission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkSignatures(String pkg1, String pkg2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkSignatures(int uid1, int uid2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPackagesForUid(int uid) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameForUid(int uid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNamesForUids(int[] uids) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUidForSharedUser(String sharedUserName) throws NameNotFoundException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ApplicationInfo> getInstalledApplications(int flags) {
|
||||
return getInstalledPackages(flags).stream().map((it) -> it.applicationInfo).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
|
||||
return getInstalledApplications(flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InstantAppInfo> getInstantApps() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getInstantAppIcon(String packageName) {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstantApp() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstantApp(String packageName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstantAppCookieMaxBytes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstantAppCookieMaxSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getInstantAppCookie() {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearInstantAppCookie() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateInstantAppCookie(byte[] cookie) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setInstantAppCookie(byte[] cookie) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSystemSharedLibraryNames() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SharedLibraryInfo> getSharedLibraries(int flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SharedLibraryInfo> getSharedLibrariesAsUser(int flags, int userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServicesSystemSharedLibraryPackageName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSharedSystemSharedLibraryPackageName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChangedPackages getChangedPackages(int sequenceNumber) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureInfo[] getSystemAvailableFeatures() {
|
||||
return new FeatureInfo[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSystemFeature(String name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSystemFeature(String name, int version) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResolveInfo resolveActivity(Intent intent, int flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics, Intent intent, int flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResolveInfo resolveService(Intent intent, int flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryIntentContentProvidersAsUser(Intent intent, int flags, int userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderInfo resolveContentProvider(String name, int flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstrumentationInfo getInstrumentationInfo(ComponentName className, int flags) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InstrumentationInfo> queryInstrumentation(String targetPackage, int flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getDrawable(String packageName, int resid, ApplicationInfo appInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getActivityIcon(ComponentName activityName) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getActivityIcon(Intent intent) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getActivityBanner(ComponentName activityName) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getActivityBanner(Intent intent) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getDefaultActivityIcon() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getApplicationIcon(ApplicationInfo info) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getApplicationIcon(String packageName) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getApplicationBanner(ApplicationInfo info) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getApplicationBanner(String packageName) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getActivityLogo(ComponentName activityName) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getActivityLogo(Intent intent) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getApplicationLogo(ApplicationInfo info) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getApplicationLogo(String packageName) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getUserBadgedIcon(Drawable icon, UserHandle user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getUserBadgedDrawableForDensity(Drawable drawable, UserHandle user, Rect badgeLocation, int badgeDensity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getUserBadgeForDensity(UserHandle user, int density) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getUserBadgeForDensityNoBackground(UserHandle user, int density) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getUserBadgedLabel(CharSequence label, UserHandle user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getText(String packageName, int resid, ApplicationInfo appInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlResourceParser getXml(String packageName, int resid, ApplicationInfo appInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getApplicationLabel(ApplicationInfo info) {
|
||||
return info.nonLocalizedLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResourcesForActivity(ComponentName activityName) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResourcesForApplication(ApplicationInfo app) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResourcesForApplication(String appPackageName) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResourcesForApplicationAsUser(String appPackageName, int userId) throws NameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installPackage(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int installExistingPackage(String packageName) throws NameNotFoundException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int installExistingPackage(String packageName, int installReason) throws NameNotFoundException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int installExistingPackageAsUser(String packageName, int userId) throws NameNotFoundException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verifyPendingInstall(int id, int verificationCode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verifyIntentFilter(int verificationId, int verificationCode, List<String> failedDomains) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntentVerificationStatusAsUser(String packageName, int userId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateIntentVerificationStatusAsUser(String packageName, int status, int userId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IntentFilter> getAllIntentFilters(String packageName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultBrowserPackageNameAsUser(int userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInstallerPackageName(String targetPackage, String installerPackageName) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpdateAvailable(String packageName, boolean updateAvaialble) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInstallerPackageName(String packageName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void freeStorage(String volumeUuid, long freeStorageSize, IntentSender pi) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPackageToPreferred(String packageName) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePackageFromPreferred(String packageName) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PackageInfo> getPreferredPackages(int flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearPackagePreferredActivities(String packageName) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreferredActivities(List<IntentFilter> outFilters, List<ComponentName> outActivities, String packageName) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getComponentEnabledSetting(ComponentName componentName) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEnabledSetting(String packageName, int newState, int flags) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getApplicationEnabledSetting(String packageName) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushPackageRestrictionsAsUser(int userId) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, UserHandle userHandle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getApplicationHiddenSettingAsUser(String packageName, UserHandle userHandle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSafeMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOnPermissionsChangeListener(OnPermissionsChangedListener listener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOnPermissionsChangeListener(OnPermissionsChangedListener listener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySet getKeySetByAlias(String packageName, String alias) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySet getSigningKeySet(String packageName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSignedBy(String packageName, KeySet ks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSignedByExactly(String packageName, KeySet ks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended, int userId) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPackageSuspendedForUser(String packageName, int userId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMoveStatus(int moveId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerMoveCallback(MoveCallback callback, Handler handler) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterMoveCallback(MoveCallback callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpgrade() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageInstaller getPackageInstaller() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, int targetUserId, int flags) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCrossProfileIntentFilters(int sourceUserId) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable loadUnbadgedItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPackageAvailable(String packageName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstallReason(String packageName, UserHandle user) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRequestPackageInstalls() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentName getInstantAppResolverSettingsComponent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentName getInstantAppInstallerComponent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInstantAppAndroidId(String packageName, UserHandle user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDexModule(String dexModulePath, DexModuleRegisterCallback callback) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package xyz.nulldev.androidcompat.bytecode
|
||||
|
||||
import javassist.CtClass
|
||||
import mu.KotlinLogging
|
||||
|
||||
/**
|
||||
* Applies Javassist modifications
|
||||
*/
|
||||
|
||||
class ModApplier {
|
||||
|
||||
val logger = KotlinLogging.logger {}
|
||||
|
||||
fun apply() {
|
||||
logger.info { "Applying Javassist mods..." }
|
||||
val modifiedClasses = mutableListOf<CtClass>()
|
||||
|
||||
modifiedClasses.forEach {
|
||||
it.toClass()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package xyz.nulldev.androidcompat.config
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import xyz.nulldev.ts.config.ConfigModule
|
||||
|
||||
/**
|
||||
* Application info config.
|
||||
*/
|
||||
|
||||
class ApplicationInfoConfigModule(config: Config) : ConfigModule(config) {
|
||||
val packageName = config.getString("packageName")!!
|
||||
val debug = config.getBoolean("debug")
|
||||
|
||||
companion object {
|
||||
fun register(config: Config)
|
||||
= ApplicationInfoConfigModule(config.getConfig("android.app"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package xyz.nulldev.androidcompat.config
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import xyz.nulldev.ts.config.ConfigModule
|
||||
|
||||
/**
|
||||
* Files configuration modules. Specifies where to store the Android files.
|
||||
*/
|
||||
|
||||
class FilesConfigModule(config: Config) : ConfigModule(config) {
|
||||
val dataDir = config.getString("dataDir")!!
|
||||
val filesDir = config.getString("filesDir")!!
|
||||
val noBackupFilesDir = config.getString("noBackupFilesDir")!!
|
||||
val externalFilesDirs: MutableList<String> = config.getStringList("externalFilesDirs")!!
|
||||
val obbDirs: MutableList<String> = config.getStringList("obbDirs")!!
|
||||
val cacheDir = config.getString("cacheDir")!!
|
||||
val codeCacheDir = config.getString("codeCacheDir")!!
|
||||
val externalCacheDirs: MutableList<String> = config.getStringList("externalCacheDirs")!!
|
||||
val externalMediaDirs: MutableList<String> = config.getStringList("externalMediaDirs")!!
|
||||
val rootDir = config.getString("rootDir")!!
|
||||
val externalStorageDir = config.getString("externalStorageDir")!!
|
||||
val downloadCacheDir = config.getString("downloadCacheDir")!!
|
||||
val databasesDir = config.getString("databasesDir")!!
|
||||
|
||||
val prefsDir = config.getString("prefsDir")!!
|
||||
|
||||
val packageDir = config.getString("packageDir")!!
|
||||
|
||||
companion object {
|
||||
fun register(config: Config)
|
||||
= FilesConfigModule(config.getConfig("android.files"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package xyz.nulldev.androidcompat.config
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import xyz.nulldev.ts.config.ConfigModule
|
||||
|
||||
class SystemConfigModule(val config: Config) : ConfigModule(config) {
|
||||
val isDebuggable = config.getBoolean("isDebuggable")
|
||||
|
||||
val propertyPrefix = "properties."
|
||||
|
||||
fun getStringProperty(property: String) = config.getString("$propertyPrefix$property")!!
|
||||
fun getIntProperty(property: String) = config.getInt("$propertyPrefix$property")
|
||||
fun getLongProperty(property: String) = config.getLong("$propertyPrefix$property")
|
||||
fun getBooleanProperty(property: String) = config.getBoolean("$propertyPrefix$property")
|
||||
fun hasProperty(property: String) = config.hasPath("$propertyPrefix$property")
|
||||
|
||||
companion object {
|
||||
fun register(config: Config)
|
||||
= SystemConfigModule(config.getConfig("android.system"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,841 @@
|
||||
package xyz.nulldev.androidcompat.db
|
||||
|
||||
import java.io.InputStream
|
||||
import java.io.Reader
|
||||
import java.math.BigDecimal
|
||||
import java.net.URL
|
||||
import java.sql.*
|
||||
import java.sql.Array
|
||||
import java.sql.Date
|
||||
import java.util.*
|
||||
|
||||
class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
|
||||
private val cachedContent = mutableListOf<ResultSetEntry>()
|
||||
private val columnCache = mutableMapOf<String, Int>()
|
||||
private var lastReturnWasNull = false
|
||||
private var cursor = 0
|
||||
var resultSetLength = 0
|
||||
|
||||
val parentMetadata = parent.metaData
|
||||
val columnCount = parentMetadata.columnCount
|
||||
val columnLabels = (1 .. columnCount).map {
|
||||
parentMetadata.getColumnLabel(it)
|
||||
}.toTypedArray()
|
||||
|
||||
init {
|
||||
val columnCount = columnCount
|
||||
|
||||
// TODO
|
||||
// Profiling reveals that this is a bottleneck (average ms for this call is: 48ms)
|
||||
// How can we optimize this?
|
||||
// We need to fill the cache as the set is loaded
|
||||
|
||||
//Fill cache
|
||||
while(parent.next()) {
|
||||
cachedContent += ResultSetEntry().apply {
|
||||
for(i in 1 .. columnCount)
|
||||
data += parent.getObject(i)
|
||||
}
|
||||
resultSetLength++
|
||||
}
|
||||
}
|
||||
|
||||
private fun notImplemented(): Nothing {
|
||||
throw UnsupportedOperationException("This class currently does not support this operation!")
|
||||
}
|
||||
|
||||
private fun cursorValid(): Boolean {
|
||||
return isAfterLast || isBeforeFirst
|
||||
}
|
||||
|
||||
private fun internalMove(row: Int) {
|
||||
if(cursor < 0) cursor = 0
|
||||
else if(cursor > resultSetLength + 1) cursor = resultSetLength + 1
|
||||
else cursor = row
|
||||
}
|
||||
|
||||
private fun obj(column: Int): Any? {
|
||||
val obj = cachedContent[cursor - 1].data[column - 1]
|
||||
lastReturnWasNull = obj == null
|
||||
return obj
|
||||
}
|
||||
|
||||
private fun obj(column: String?): Any? {
|
||||
return obj(cachedFindColumn(column))
|
||||
}
|
||||
|
||||
private fun cachedFindColumn(column: String?)
|
||||
= columnCache.getOrPut(column!!, {
|
||||
findColumn(column)
|
||||
})
|
||||
|
||||
override fun getNClob(columnIndex: Int): NClob {
|
||||
return obj(columnIndex) as NClob
|
||||
}
|
||||
|
||||
override fun getNClob(columnLabel: String?): NClob {
|
||||
return obj(columnLabel) as NClob
|
||||
}
|
||||
|
||||
override fun updateNString(columnIndex: Int, nString: String?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateNString(columnLabel: String?, nString: String?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBinaryStream(columnIndex: Int, x: InputStream?, length: Int) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBinaryStream(columnLabel: String?, x: InputStream?, length: Int) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBinaryStream(columnIndex: Int, x: InputStream?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBinaryStream(columnLabel: String?, x: InputStream?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBinaryStream(columnIndex: Int, x: InputStream?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBinaryStream(columnLabel: String?, x: InputStream?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateTimestamp(columnIndex: Int, x: Timestamp?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateTimestamp(columnLabel: String?, x: Timestamp?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateNCharacterStream(columnIndex: Int, x: Reader?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateNCharacterStream(columnLabel: String?, reader: Reader?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateNCharacterStream(columnIndex: Int, x: Reader?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateNCharacterStream(columnLabel: String?, reader: Reader?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateInt(columnIndex: Int, x: Int) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateInt(columnLabel: String?, x: Int) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun moveToInsertRow() {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getDate(columnIndex: Int): Date {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getDate(columnLabel: String?): Date {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getDate(columnIndex: Int, cal: Calendar?): Date {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getDate(columnLabel: String?, cal: Calendar?): Date {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun beforeFirst() {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateFloat(columnIndex: Int, x: Float) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateFloat(columnLabel: String?, x: Float) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getBoolean(columnIndex: Int): Boolean {
|
||||
return obj(columnIndex) as Boolean
|
||||
}
|
||||
|
||||
override fun getBoolean(columnLabel: String?): Boolean {
|
||||
return obj(columnLabel) as Boolean
|
||||
}
|
||||
|
||||
override fun isFirst(): Boolean {
|
||||
return cursor - 1 < resultSetLength
|
||||
}
|
||||
|
||||
override fun getBigDecimal(columnIndex: Int, scale: Int): BigDecimal {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getBigDecimal(columnLabel: String?, scale: Int): BigDecimal {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getBigDecimal(columnIndex: Int): BigDecimal {
|
||||
return obj(columnIndex) as BigDecimal
|
||||
}
|
||||
|
||||
override fun getBigDecimal(columnLabel: String?): BigDecimal {
|
||||
return obj(columnLabel) as BigDecimal
|
||||
}
|
||||
|
||||
override fun updateBytes(columnIndex: Int, x: ByteArray?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBytes(columnLabel: String?, x: ByteArray?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun isLast(): Boolean {
|
||||
return cursor == resultSetLength
|
||||
}
|
||||
|
||||
override fun insertRow() {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getTime(columnIndex: Int): Time {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getTime(columnLabel: String?): Time {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getTime(columnIndex: Int, cal: Calendar?): Time {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getTime(columnLabel: String?, cal: Calendar?): Time {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun rowDeleted() = false
|
||||
|
||||
override fun last(): Boolean {
|
||||
internalMove(resultSetLength)
|
||||
return cursorValid()
|
||||
}
|
||||
|
||||
override fun isAfterLast(): Boolean {
|
||||
return cursor > resultSetLength
|
||||
}
|
||||
|
||||
override fun relative(rows: Int): Boolean {
|
||||
internalMove(cursor + rows)
|
||||
return cursorValid()
|
||||
}
|
||||
|
||||
override fun absolute(row: Int): Boolean {
|
||||
if(row > 0) {
|
||||
internalMove(row)
|
||||
} else {
|
||||
last()
|
||||
for(i in 1 .. row)
|
||||
previous()
|
||||
}
|
||||
return cursorValid()
|
||||
}
|
||||
|
||||
override fun getSQLXML(columnIndex: Int): SQLXML? {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getSQLXML(columnLabel: String?): SQLXML? {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun <T : Any?> unwrap(iface: Class<T>?): T {
|
||||
if(thisIsWrapperFor(iface))
|
||||
return this as T
|
||||
else
|
||||
return parent.unwrap(iface)
|
||||
}
|
||||
|
||||
override fun next(): Boolean {
|
||||
internalMove(cursor + 1)
|
||||
return cursorValid()
|
||||
}
|
||||
|
||||
override fun getFloat(columnIndex: Int): Float {
|
||||
return obj(columnIndex) as Float
|
||||
}
|
||||
|
||||
override fun getFloat(columnLabel: String?): Float {
|
||||
return obj(columnLabel) as Float
|
||||
}
|
||||
|
||||
override fun wasNull() = lastReturnWasNull
|
||||
|
||||
override fun getRow(): Int {
|
||||
return cursor
|
||||
}
|
||||
|
||||
override fun first(): Boolean {
|
||||
internalMove(1)
|
||||
return cursorValid()
|
||||
}
|
||||
|
||||
override fun updateAsciiStream(columnIndex: Int, x: InputStream?, length: Int) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateAsciiStream(columnLabel: String?, x: InputStream?, length: Int) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateAsciiStream(columnIndex: Int, x: InputStream?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateAsciiStream(columnLabel: String?, x: InputStream?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateAsciiStream(columnIndex: Int, x: InputStream?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateAsciiStream(columnLabel: String?, x: InputStream?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getURL(columnIndex: Int): URL {
|
||||
return obj(columnIndex) as URL
|
||||
}
|
||||
|
||||
override fun getURL(columnLabel: String?): URL {
|
||||
return obj(columnLabel) as URL
|
||||
}
|
||||
|
||||
override fun updateShort(columnIndex: Int, x: Short) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateShort(columnLabel: String?, x: Short) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getType() = ResultSet.TYPE_SCROLL_INSENSITIVE
|
||||
|
||||
override fun updateNClob(columnIndex: Int, nClob: NClob?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateNClob(columnLabel: String?, nClob: NClob?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateNClob(columnIndex: Int, reader: Reader?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateNClob(columnLabel: String?, reader: Reader?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateNClob(columnIndex: Int, reader: Reader?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateNClob(columnLabel: String?, reader: Reader?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateRef(columnIndex: Int, x: Ref?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateRef(columnLabel: String?, x: Ref?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateObject(columnIndex: Int, x: Any?, scaleOrLength: Int) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateObject(columnIndex: Int, x: Any?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateObject(columnLabel: String?, x: Any?, scaleOrLength: Int) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateObject(columnLabel: String?, x: Any?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun afterLast() {
|
||||
internalMove(resultSetLength + 1)
|
||||
}
|
||||
|
||||
override fun updateLong(columnIndex: Int, x: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateLong(columnLabel: String?, x: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getBlob(columnIndex: Int): Blob {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getBlob(columnLabel: String?): Blob {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateClob(columnIndex: Int, x: Clob?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateClob(columnLabel: String?, x: Clob?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateClob(columnIndex: Int, reader: Reader?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateClob(columnLabel: String?, reader: Reader?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateClob(columnIndex: Int, reader: Reader?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateClob(columnLabel: String?, reader: Reader?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getByte(columnIndex: Int): Byte {
|
||||
return obj(columnIndex) as Byte
|
||||
}
|
||||
|
||||
override fun getByte(columnLabel: String?): Byte {
|
||||
return obj(columnLabel) as Byte
|
||||
}
|
||||
|
||||
override fun getString(columnIndex: Int): String? {
|
||||
return obj(columnIndex) as String?
|
||||
}
|
||||
|
||||
override fun getString(columnLabel: String?): String? {
|
||||
return obj(columnLabel) as String?
|
||||
}
|
||||
|
||||
override fun updateSQLXML(columnIndex: Int, xmlObject: SQLXML?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateSQLXML(columnLabel: String?, xmlObject: SQLXML?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateDate(columnIndex: Int, x: Date?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateDate(columnLabel: String?, x: Date?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getObject(columnIndex: Int): Any? {
|
||||
return obj(columnIndex)
|
||||
}
|
||||
|
||||
override fun getObject(columnLabel: String?): Any? {
|
||||
return obj(columnLabel)
|
||||
}
|
||||
|
||||
override fun getObject(columnIndex: Int, map: MutableMap<String, Class<*>>?): Any {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getObject(columnLabel: String?, map: MutableMap<String, Class<*>>?): Any {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun <T : Any?> getObject(columnIndex: Int, type: Class<T>?): T {
|
||||
return obj(columnIndex) as T
|
||||
}
|
||||
|
||||
override fun <T : Any?> getObject(columnLabel: String?, type: Class<T>?): T {
|
||||
return obj(columnLabel) as T
|
||||
}
|
||||
|
||||
override fun previous(): Boolean {
|
||||
internalMove(cursor - 1)
|
||||
return cursorValid()
|
||||
}
|
||||
|
||||
override fun updateDouble(columnIndex: Int, x: Double) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateDouble(columnLabel: String?, x: Double) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
private fun castToLong(obj: Any?): Long {
|
||||
if(obj == null) return 0
|
||||
else if(obj is Long) return obj
|
||||
else if(obj is Number) return obj.toLong()
|
||||
else throw IllegalStateException("Object is not a long!")
|
||||
}
|
||||
|
||||
override fun getLong(columnIndex: Int): Long {
|
||||
return castToLong(obj(columnIndex))
|
||||
}
|
||||
|
||||
override fun getLong(columnLabel: String?): Long {
|
||||
return castToLong(obj(columnLabel))
|
||||
}
|
||||
|
||||
override fun getClob(columnIndex: Int): Clob {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getClob(columnLabel: String?): Clob {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBlob(columnIndex: Int, x: Blob?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBlob(columnLabel: String?, x: Blob?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBlob(columnIndex: Int, inputStream: InputStream?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBlob(columnLabel: String?, inputStream: InputStream?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBlob(columnIndex: Int, inputStream: InputStream?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBlob(columnLabel: String?, inputStream: InputStream?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateByte(columnIndex: Int, x: Byte) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateByte(columnLabel: String?, x: Byte) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateRow() {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun deleteRow() {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getNString(columnIndex: Int): String {
|
||||
return obj(columnIndex) as String
|
||||
}
|
||||
|
||||
override fun getNString(columnLabel: String?): String {
|
||||
return obj(columnLabel) as String
|
||||
}
|
||||
|
||||
override fun getArray(columnIndex: Int): Array {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getArray(columnLabel: String?): Array {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun cancelRowUpdates() {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateString(columnIndex: Int, x: String?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateString(columnLabel: String?, x: String?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun setFetchDirection(direction: Int) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getCharacterStream(columnIndex: Int): Reader {
|
||||
return getNCharacterStream(columnIndex)
|
||||
}
|
||||
|
||||
override fun getCharacterStream(columnLabel: String?): Reader {
|
||||
return getNCharacterStream(columnLabel)
|
||||
}
|
||||
|
||||
override fun isBeforeFirst(): Boolean {
|
||||
return cursor - 1 < resultSetLength
|
||||
}
|
||||
|
||||
override fun updateBoolean(columnIndex: Int, x: Boolean) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBoolean(columnLabel: String?, x: Boolean) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun refreshRow() {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun rowUpdated() = false
|
||||
|
||||
override fun updateBigDecimal(columnIndex: Int, x: BigDecimal?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateBigDecimal(columnLabel: String?, x: BigDecimal?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getShort(columnIndex: Int): Short {
|
||||
return obj(columnIndex) as Short
|
||||
}
|
||||
|
||||
override fun getShort(columnLabel: String?): Short {
|
||||
return obj(columnLabel) as Short
|
||||
}
|
||||
|
||||
override fun getAsciiStream(columnIndex: Int): InputStream {
|
||||
return getBinaryStream(columnIndex)
|
||||
}
|
||||
|
||||
override fun getAsciiStream(columnLabel: String?): InputStream {
|
||||
return getBinaryStream(columnLabel)
|
||||
}
|
||||
|
||||
override fun updateTime(columnIndex: Int, x: Time?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateTime(columnLabel: String?, x: Time?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getTimestamp(columnIndex: Int): Timestamp {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getTimestamp(columnLabel: String?): Timestamp {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getTimestamp(columnIndex: Int, cal: Calendar?): Timestamp {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getTimestamp(columnLabel: String?, cal: Calendar?): Timestamp {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getRef(columnIndex: Int): Ref {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getRef(columnLabel: String?): Ref {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getConcurrency() = ResultSet.CONCUR_READ_ONLY
|
||||
|
||||
override fun updateRowId(columnIndex: Int, x: RowId?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateRowId(columnLabel: String?, x: RowId?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getNCharacterStream(columnIndex: Int): Reader {
|
||||
return getBinaryStream(columnIndex).reader()
|
||||
}
|
||||
|
||||
override fun getNCharacterStream(columnLabel: String?): Reader {
|
||||
return getBinaryStream(columnLabel).reader()
|
||||
}
|
||||
|
||||
override fun updateArray(columnIndex: Int, x: Array?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateArray(columnLabel: String?, x: Array?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getBytes(columnIndex: Int): ByteArray {
|
||||
return obj(columnIndex) as ByteArray
|
||||
}
|
||||
|
||||
override fun getBytes(columnLabel: String?): ByteArray {
|
||||
return obj(columnLabel) as ByteArray
|
||||
}
|
||||
|
||||
override fun getDouble(columnIndex: Int): Double {
|
||||
return obj(columnIndex) as Double
|
||||
}
|
||||
|
||||
override fun getDouble(columnLabel: String?): Double {
|
||||
return obj(columnLabel) as Double
|
||||
}
|
||||
|
||||
override fun getUnicodeStream(columnIndex: Int): InputStream {
|
||||
return getBinaryStream(columnIndex)
|
||||
}
|
||||
|
||||
override fun getUnicodeStream(columnLabel: String?): InputStream {
|
||||
return getBinaryStream(columnLabel)
|
||||
}
|
||||
|
||||
override fun rowInserted() = false
|
||||
|
||||
private fun thisIsWrapperFor(iface: Class<*>?) = this.javaClass.isInstance(iface)
|
||||
|
||||
override fun isWrapperFor(iface: Class<*>?): Boolean {
|
||||
return thisIsWrapperFor(iface) || parent.isWrapperFor(iface)
|
||||
}
|
||||
|
||||
override fun getInt(columnIndex: Int): Int {
|
||||
return obj(columnIndex) as Int
|
||||
}
|
||||
|
||||
override fun getInt(columnLabel: String?): Int {
|
||||
return obj(columnLabel) as Int
|
||||
}
|
||||
|
||||
override fun updateNull(columnIndex: Int) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateNull(columnLabel: String?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getRowId(columnIndex: Int): RowId {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getRowId(columnLabel: String?): RowId {
|
||||
//TODO Maybe?
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getMetaData(): ResultSetMetaData {
|
||||
return object : ResultSetMetaData by parentMetadata {
|
||||
override fun isReadOnly(column: Int) = true
|
||||
override fun isWritable(column: Int) = false
|
||||
override fun isDefinitelyWritable(column: Int) = false
|
||||
override fun getColumnCount() = this@ScrollableResultSet.columnCount
|
||||
override fun getColumnLabel(column: Int): String {
|
||||
return columnLabels[column - 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBinaryStream(columnIndex: Int): InputStream {
|
||||
return (obj(columnIndex) as ByteArray).inputStream()
|
||||
}
|
||||
|
||||
override fun getBinaryStream(columnLabel: String?): InputStream {
|
||||
return (obj(columnLabel) as ByteArray).inputStream()
|
||||
}
|
||||
|
||||
override fun updateCharacterStream(columnIndex: Int, x: Reader?, length: Int) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateCharacterStream(columnLabel: String?, reader: Reader?, length: Int) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateCharacterStream(columnIndex: Int, x: Reader?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateCharacterStream(columnLabel: String?, reader: Reader?, length: Long) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateCharacterStream(columnIndex: Int, x: Reader?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun updateCharacterStream(columnLabel: String?, reader: Reader?) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
class ResultSetEntry {
|
||||
val data = mutableListOf<Any?>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package xyz.nulldev.androidcompat.info
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import xyz.nulldev.androidcompat.config.ApplicationInfoConfigModule
|
||||
import xyz.nulldev.ts.config.ConfigManager
|
||||
|
||||
class ApplicationInfoImpl(override val di: DI = DI.global) : ApplicationInfo(), DIAware {
|
||||
val configManager: ConfigManager by di.instance()
|
||||
|
||||
val appInfoConfig: ApplicationInfoConfigModule
|
||||
get() = configManager.module()
|
||||
|
||||
val debug: Boolean get() = appInfoConfig.debug
|
||||
|
||||
init {
|
||||
super.packageName = appInfoConfig.packageName
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package xyz.nulldev.androidcompat.io
|
||||
|
||||
import xyz.nulldev.androidcompat.config.FilesConfigModule
|
||||
import xyz.nulldev.ts.config.ConfigManager
|
||||
import xyz.nulldev.ts.config.GlobalConfigManager
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Android file constants.
|
||||
*/
|
||||
class AndroidFiles(val configManager: ConfigManager = GlobalConfigManager) {
|
||||
val filesConfig: FilesConfigModule
|
||||
get() = configManager.module()
|
||||
|
||||
val dataDir: File get() = registerFile(filesConfig.dataDir)
|
||||
val filesDir: File get() = registerFile(filesConfig.filesDir)
|
||||
val noBackupFilesDir: File get() = registerFile(filesConfig.noBackupFilesDir)
|
||||
val externalFilesDirs: List<File> get() = filesConfig.externalFilesDirs.map { registerFile(it) }
|
||||
val obbDirs: List<File> get() = filesConfig.obbDirs.map { registerFile(it) }
|
||||
val cacheDir: File get() = registerFile(filesConfig.cacheDir)
|
||||
val codeCacheDir: File get() = registerFile(filesConfig.codeCacheDir)
|
||||
val externalCacheDirs: List<File> get() = filesConfig.externalCacheDirs.map { registerFile(it) }
|
||||
val externalMediaDirs: List<File> get() = filesConfig.externalMediaDirs.map { registerFile(it) }
|
||||
val rootDir: File get() = registerFile(filesConfig.rootDir)
|
||||
val externalStorageDir: File get() = registerFile(filesConfig.externalStorageDir)
|
||||
val downloadCacheDir: File get() = registerFile(filesConfig.downloadCacheDir)
|
||||
val databasesDir: File get() = registerFile(filesConfig.databasesDir)
|
||||
|
||||
val prefsDir: File get() = registerFile(filesConfig.prefsDir)
|
||||
|
||||
val packagesDir: File get() = registerFile(filesConfig.packageDir)
|
||||
|
||||
fun registerFile(file: String): File {
|
||||
return File(file).apply {
|
||||
mkdirs()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* Copyright 2016 Andy Bao
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package xyz.nulldev.androidcompat.io.sharedprefs;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* JSON implementation of Android's SharedPreferences.
|
||||
*/
|
||||
public class JsonSharedPreferences implements SharedPreferences {
|
||||
|
||||
private static final String KEY_TYPE = "t";
|
||||
private static final String KEY_VALUE = "v";
|
||||
|
||||
private Map<String, Object> prefs = new HashMap<>(); //In-memory preference values
|
||||
private List<OnSharedPreferenceChangeListener> listeners = new ArrayList<>(); //Change listeners
|
||||
private File file; //Where the values should be stored
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(JsonSharedPreferences.class);
|
||||
|
||||
/**
|
||||
* Create a SharedPreference instance from a file
|
||||
* @param file The file to load the prefs from, use null to create an empty SharedPreferences instance.
|
||||
*/
|
||||
public JsonSharedPreferences(File file) {
|
||||
this.file = file;
|
||||
//Load previous values if they exist
|
||||
if(file != null) {
|
||||
if(file.exists()) {
|
||||
try {
|
||||
loadFromString(new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed to read shared prefs from String!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the SharedPreferences from a String.
|
||||
* @param string The String to load the prefs from.
|
||||
*/
|
||||
public synchronized void loadFromString(String string) {
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(string);
|
||||
//Changes are need to be applied atomically so we temporarily store all changes in a separate map
|
||||
Map<String, Object> tempMap = new HashMap<>();
|
||||
//Loop through all preference objects in JSON
|
||||
for (String key : jsonObject.keySet()) {
|
||||
JSONObject object = jsonObject.getJSONObject(key);
|
||||
//Load the object's Java type
|
||||
String typeString = object.getString(KEY_TYPE);
|
||||
PrefType type = PrefType.valueOf(typeString);
|
||||
Object res;
|
||||
//Map the JSON object's value to it's Java value
|
||||
switch (type) {
|
||||
case String:
|
||||
res = object.getString(KEY_VALUE);
|
||||
break;
|
||||
case StringSet:
|
||||
Set<String> set = new HashSet<>();
|
||||
JSONArray array = object.getJSONArray(KEY_VALUE);
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
set.add(array.getString(i));
|
||||
}
|
||||
res = set;
|
||||
break;
|
||||
case Int:
|
||||
res = object.getInt(KEY_VALUE);
|
||||
break;
|
||||
case Long:
|
||||
res = object.getLong(KEY_VALUE);
|
||||
break;
|
||||
case Float:
|
||||
res = Float.parseFloat(object.getString(KEY_VALUE));
|
||||
break;
|
||||
case Boolean:
|
||||
res = object.getBoolean(KEY_VALUE);
|
||||
break;
|
||||
default:
|
||||
case Null:
|
||||
res = null;
|
||||
break;
|
||||
}
|
||||
//Queue the loaded object for placement into the in-memory preference map
|
||||
tempMap.put(key, res);
|
||||
}
|
||||
//Apply all changes made to the in-memory preference map atomically
|
||||
prefs = tempMap;
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException("Error parsing JSON shared preferences!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the JSON prefs to a String.
|
||||
* @return The serialized prefs.
|
||||
*/
|
||||
public synchronized String saveToString() {
|
||||
return saveToJSONObject().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the JSON prefs to a JSONObject
|
||||
* @return The serialized prefs.
|
||||
*/
|
||||
public synchronized JSONObject saveToJSONObject() {
|
||||
JSONObject object = new JSONObject();
|
||||
//Loop through every preference in the in-memory preference map
|
||||
for (Map.Entry<String, Object> entry : prefs.entrySet()) {
|
||||
JSONObject entryObj = new JSONObject();
|
||||
Object value = entry.getValue();
|
||||
//Determine the object's type
|
||||
PrefType type = PrefType.fromObject(value);
|
||||
if (type == PrefType.Float) {
|
||||
value = value.toString();
|
||||
} else if (type == PrefType.StringSet) {
|
||||
JSONArray arrayObj = new JSONArray();
|
||||
Set<String> casted = (Set<String>) value;
|
||||
for (String item : casted) {
|
||||
arrayObj.put(item);
|
||||
}
|
||||
value = arrayObj;
|
||||
}
|
||||
//Put the preference's type and value into a JSON object
|
||||
entryObj.put(KEY_TYPE, type.name());
|
||||
entryObj.put(KEY_VALUE, value);
|
||||
object.put(entry.getKey(), entryObj);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Map<String, ?> getAll() {
|
||||
return new HashMap<>(prefs);
|
||||
}
|
||||
|
||||
private <T> T fallbackIfNull(T obj, T fallback) {
|
||||
if (obj == null) {
|
||||
return fallback;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getString(String s, String s1) {
|
||||
return fallbackIfNull((String) prefs.get(s), s1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Set<String> getStringSet(String s, Set<String> set) {
|
||||
return fallbackIfNull((Set<String>) prefs.get(s), set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getInt(String s, int i) {
|
||||
return fallbackIfNull((Integer) prefs.get(s), i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized long getLong(String s, long l) {
|
||||
return fallbackIfNull((Long) prefs.get(s), l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized float getFloat(String s, float v) {
|
||||
return fallbackIfNull((Float) prefs.get(s), v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean getBoolean(String s, boolean b) {
|
||||
return fallbackIfNull((Boolean) prefs.get(s), b);
|
||||
}
|
||||
|
||||
public synchronized Object get(String s, Object b) {
|
||||
return fallbackIfNull(prefs.get(s), b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean contains(String s) {
|
||||
return prefs.containsKey(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonSharedPreferencesEditor edit() {
|
||||
return new JsonSharedPreferencesEditor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void registerOnSharedPreferenceChangeListener(
|
||||
OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
|
||||
if (!listeners.contains(onSharedPreferenceChangeListener)) {
|
||||
listeners.add(onSharedPreferenceChangeListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void unregisterOnSharedPreferenceChangeListener(
|
||||
OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
|
||||
listeners.remove(onSharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
public class JsonSharedPreferencesEditor implements Editor {
|
||||
|
||||
Map<String, Object> prefsClone = new HashMap<>(prefs);
|
||||
|
||||
List<String> affectedKeys = new ArrayList<>(); //List of all affected keys to invoke listeners on once changes applied
|
||||
|
||||
private JsonSharedPreferencesEditor() {
|
||||
}
|
||||
|
||||
private void recordChange(String key) {
|
||||
if (!affectedKeys.contains(key)) {
|
||||
affectedKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized Editor put(String s, Object o) {
|
||||
PrefType.fromObject(o); // Will throw if 'o' is invalid
|
||||
|
||||
prefsClone.put(s, o);
|
||||
recordChange(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Editor putString(String s, String s1) {
|
||||
prefsClone.put(s, s1);
|
||||
recordChange(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Editor putStringSet(String s, Set<String> set) {
|
||||
Set<String> clonedSet = new HashSet<>();
|
||||
clonedSet.addAll(set);
|
||||
prefsClone.put(s, clonedSet);
|
||||
recordChange(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Editor putInt(String s, int i) {
|
||||
prefsClone.put(s, i);
|
||||
recordChange(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Editor putLong(String s, long l) {
|
||||
prefsClone.put(s, l);
|
||||
recordChange(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Editor putFloat(String s, float v) {
|
||||
prefsClone.put(s, v);
|
||||
recordChange(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Editor putBoolean(String s, boolean b) {
|
||||
prefsClone.put(s, b);
|
||||
recordChange(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Editor remove(String s) {
|
||||
prefsClone.remove(s);
|
||||
recordChange(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Editor clear() {
|
||||
prefsClone.keySet().forEach(this::recordChange);
|
||||
prefsClone.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean commit() {
|
||||
synchronized (JsonSharedPreferences.this) {
|
||||
//Backup prefs
|
||||
Map<String, Object> oldPrefs = prefs;
|
||||
prefs = prefsClone;
|
||||
if(file != null) {
|
||||
//Delete old on-disk copy of preferences
|
||||
if(file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
//Save new preferences to disk
|
||||
String string = saveToString();
|
||||
try (PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)))) {
|
||||
writer.print(string);
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed to save shared prefs!", e);
|
||||
prefs = oldPrefs;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//Invoke preference change listeners
|
||||
for (String key : affectedKeys) {
|
||||
for (OnSharedPreferenceChangeListener listener : listeners) {
|
||||
listener.onSharedPreferenceChanged(JsonSharedPreferences.this, key);
|
||||
}
|
||||
}
|
||||
affectedKeys.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
//TODO Threading?
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public void setFile(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/** Preference types (for storing what type of object a preference is)**/
|
||||
private enum PrefType {
|
||||
String,
|
||||
StringSet,
|
||||
Int,
|
||||
Long,
|
||||
Float,
|
||||
Boolean,
|
||||
Null;
|
||||
|
||||
public static PrefType fromObject(Object object) {
|
||||
if (object == null) {
|
||||
return Null;
|
||||
}
|
||||
if (object instanceof String) {
|
||||
return String;
|
||||
} else if (object instanceof Set || Set.class.isAssignableFrom(object.getClass())) {
|
||||
return StringSet;
|
||||
} else if (object instanceof Integer) {
|
||||
return Int;
|
||||
} else if (object instanceof Long) {
|
||||
return Long;
|
||||
} else if (object instanceof Float) {
|
||||
return Float;
|
||||
} else if (object instanceof Boolean) {
|
||||
return Boolean;
|
||||
}
|
||||
throw new IllegalArgumentException("Could not find type of object: " + object);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package xyz.nulldev.androidcompat.pm
|
||||
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.Signature
|
||||
import android.os.Bundle
|
||||
import com.android.apksig.ApkVerifier
|
||||
import com.googlecode.d2j.dex.Dex2jar
|
||||
import net.dongliu.apk.parser.ApkFile
|
||||
import net.dongliu.apk.parser.ApkParsers
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
import org.w3c.dom.NodeList
|
||||
import java.io.File
|
||||
import javax.imageio.ImageIO
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
|
||||
|
||||
|
||||
data class InstalledPackage(val root: File) {
|
||||
val apk = File(root, "package.apk")
|
||||
val jar = File(root, "translated.jar")
|
||||
val icon = File(root, "icon.png")
|
||||
|
||||
val info: PackageInfo
|
||||
get() = ApkParsers.getMetaInfo(apk).toPackageInfo(root, apk).also {
|
||||
val parsed = ApkFile(apk)
|
||||
val dbFactory = DocumentBuilderFactory.newInstance()
|
||||
val dBuilder = dbFactory.newDocumentBuilder()
|
||||
val doc = parsed.manifestXml.byteInputStream().use {
|
||||
dBuilder.parse(it)
|
||||
}
|
||||
|
||||
it.applicationInfo.metaData = Bundle().apply {
|
||||
val appTag = doc.getElementsByTagName("application").item(0)
|
||||
|
||||
appTag?.childNodes?.toList()?.filter {
|
||||
it.nodeType == Node.ELEMENT_NODE
|
||||
}?.map {
|
||||
it as Element
|
||||
}?.filter {
|
||||
it.tagName == "meta-data"
|
||||
}?.map {
|
||||
putString(it.attributes.getNamedItem("android:name").nodeValue,
|
||||
it.attributes.getNamedItem("android:value").nodeValue)
|
||||
}
|
||||
}
|
||||
|
||||
it.signatures = (parsed.apkSingers.flatMap { it.certificateMetas }
|
||||
/*+ parsed.apkV2Singers.flatMap { it.certificateMetas }*/) // Blocked by: https://github.com/hsiafan/apk-parser/issues/72
|
||||
.map { Signature(it.data) }.toTypedArray()
|
||||
}
|
||||
|
||||
fun verify(): Boolean {
|
||||
val res = ApkVerifier.Builder(apk)
|
||||
.build()
|
||||
.verify()
|
||||
|
||||
return res.isVerified
|
||||
}
|
||||
|
||||
fun writeIcon() {
|
||||
try {
|
||||
val icons = ApkFile(apk).allIcons
|
||||
|
||||
val read = icons.filter { it.isFile }.map {
|
||||
it.data.inputStream().use {
|
||||
ImageIO.read(it)
|
||||
}
|
||||
}.sortedByDescending { it.width * it.height }.firstOrNull() ?: return
|
||||
|
||||
ImageIO.write(read, "png", icon)
|
||||
} catch(e: Exception) {
|
||||
icon.delete()
|
||||
}
|
||||
}
|
||||
|
||||
fun writeJar() {
|
||||
try {
|
||||
Dex2jar.from(apk).to(jar.toPath())
|
||||
} catch(e: Exception) {
|
||||
jar.delete()
|
||||
}
|
||||
}
|
||||
|
||||
private fun NodeList.toList(): List<Node> {
|
||||
val out = mutableListOf<Node>()
|
||||
|
||||
for(i in 0 until length)
|
||||
out += item(i)
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package xyz.nulldev.androidcompat.pm
|
||||
|
||||
import net.dongliu.apk.parser.ApkParsers
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import xyz.nulldev.androidcompat.io.AndroidFiles
|
||||
import java.io.File
|
||||
|
||||
class PackageController {
|
||||
private val androidFiles by DI.global.instance<AndroidFiles>()
|
||||
private val uninstallListeners = mutableListOf<(String) -> Unit>()
|
||||
|
||||
fun registerUninstallListener(listener: (String) -> Unit) {
|
||||
uninstallListeners.add(listener)
|
||||
}
|
||||
|
||||
fun unregisterUninstallListener(listener: (String) -> Unit) {
|
||||
uninstallListeners.remove(listener)
|
||||
}
|
||||
|
||||
private fun findRoot(apk: File): File {
|
||||
val pn = ApkParsers.getMetaInfo(apk).packageName
|
||||
|
||||
return File(androidFiles.packagesDir, pn)
|
||||
}
|
||||
|
||||
fun installPackage(apk: File, allowReinstall: Boolean) {
|
||||
val root = findRoot(apk)
|
||||
|
||||
if (root.exists()) {
|
||||
if (!allowReinstall) {
|
||||
throw IllegalStateException("Package already installed!")
|
||||
} else {
|
||||
// TODO Compare past and new signature
|
||||
root.deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
root.mkdirs()
|
||||
|
||||
val installed = InstalledPackage(root)
|
||||
apk.copyTo(installed.apk)
|
||||
installed.writeIcon()
|
||||
installed.writeJar()
|
||||
|
||||
if (!installed.jar.exists()) {
|
||||
throw IllegalStateException("Failed to translate APK dex!")
|
||||
}
|
||||
} catch(t: Throwable) {
|
||||
root.deleteRecursively()
|
||||
throw t
|
||||
}
|
||||
}
|
||||
|
||||
fun listInstalled(): List<InstalledPackage> {
|
||||
return androidFiles.packagesDir.listFiles().orEmpty().filter {
|
||||
it.isDirectory
|
||||
}.map {
|
||||
InstalledPackage(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun deletePackage(pack: InstalledPackage) {
|
||||
if(!pack.root.exists()) error("Package was never installed!")
|
||||
|
||||
val packageName = pack.info.packageName
|
||||
pack.root.deleteRecursively()
|
||||
uninstallListeners.forEach {
|
||||
it(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
fun findPackage(packageName: String): InstalledPackage? {
|
||||
val file = File(androidFiles.packagesDir, packageName)
|
||||
return if(file.exists())
|
||||
InstalledPackage(file)
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
fun findJarFromApk(apkFile: File): File? {
|
||||
val pkgName = ApkParsers.getMetaInfo(apkFile).packageName
|
||||
return findPackage(pkgName)?.jar
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package xyz.nulldev.androidcompat.pm
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.FeatureInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import net.dongliu.apk.parser.bean.ApkMeta
|
||||
import java.io.File
|
||||
|
||||
fun ApkMeta.toPackageInfo(root: File, apk: File): PackageInfo {
|
||||
return PackageInfo().also {
|
||||
it.packageName = packageName
|
||||
it.versionCode = versionCode.toInt()
|
||||
it.versionName = versionName
|
||||
|
||||
it.reqFeatures = usesFeatures.map {
|
||||
FeatureInfo().apply {
|
||||
name = it.name
|
||||
}
|
||||
}.toTypedArray()
|
||||
|
||||
it.applicationInfo = ApplicationInfo().apply {
|
||||
packageName = it.packageName
|
||||
nonLocalizedLabel = label
|
||||
sourceDir = apk.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package xyz.nulldev.androidcompat.res;
|
||||
|
||||
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl;
|
||||
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* BuildConfig compat class.
|
||||
*/
|
||||
public class BuildConfigCompat {
|
||||
private static ApplicationInfoImpl applicationInfo = KodeinGlobalHelper.instance(ApplicationInfoImpl.class);
|
||||
|
||||
public static final boolean DEBUG = applicationInfo.getDebug();
|
||||
|
||||
//We assume application ID = package name
|
||||
public static final String APPLICATION_ID = applicationInfo.packageName;
|
||||
|
||||
//TODO Build time is hardcoded currently
|
||||
public static final String BUILD_TIME;
|
||||
static {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.set(2000, Calendar.JANUARY, 1);
|
||||
BUILD_TIME = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'").format(cal.getTime());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package xyz.nulldev.androidcompat.res
|
||||
|
||||
class DrawableResource(val location: String) : Resource {
|
||||
override fun getType() = DrawableResource::class.java
|
||||
|
||||
override fun getValue() = javaClass.getResourceAsStream(location)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2016 Andy Bao
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package xyz.nulldev.androidcompat.res;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Custom implementation of R.java.
|
||||
*/
|
||||
public class RCompat {
|
||||
//Resources stored in memory
|
||||
private static Map<Integer, Resource> resources = new HashMap<>();
|
||||
//The last used resource ID, used to generate new resource IDs
|
||||
private static int lastRes = 0;
|
||||
|
||||
/**
|
||||
* Add a new String resource.
|
||||
* @param s The value of the String resource.
|
||||
* @return The added String resource.
|
||||
*/
|
||||
public static int sres(String s) {
|
||||
return res(new StringResource(s));
|
||||
}
|
||||
|
||||
public static int dres(String s) {
|
||||
return res(new DrawableResource(s));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a resource.
|
||||
* @param res The resource to add.
|
||||
* @return The ID of the added resource.
|
||||
*/
|
||||
public static int res(Resource res) {
|
||||
int nextRes = lastRes++;
|
||||
resources.put(nextRes, res);
|
||||
return nextRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string resource
|
||||
* @param id The id of the resource
|
||||
* @return The string resource
|
||||
*/
|
||||
public static String getString(int id) {
|
||||
return cast(resources.get(id), StringResource.class).getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for casting resources.
|
||||
* @param resource The resource to cast
|
||||
* @param output The class of the output resource type
|
||||
* @param <T> The type of the output resource
|
||||
* @return The casted resource
|
||||
*/
|
||||
private static <T extends Resource> T cast(Resource resource, Class<T> output) {
|
||||
if(resource.getType().equals(output)) {
|
||||
return (T) resource;
|
||||
} else {
|
||||
throw new IllegalArgumentException("This resource is not of type: " + output.getSimpleName() + "!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2016 Andy Bao
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package xyz.nulldev.androidcompat.res
|
||||
|
||||
/**
|
||||
* A static Android resource.
|
||||
*/
|
||||
|
||||
interface Resource {
|
||||
fun getType(): Class<out Resource>
|
||||
|
||||
fun getValue(): Any?
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2016 Andy Bao
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package xyz.nulldev.androidcompat.res
|
||||
|
||||
/**
|
||||
* String resource.
|
||||
*/
|
||||
class StringResource(val string: String) : Resource {
|
||||
override fun getValue() = string
|
||||
|
||||
override fun getType() = StringResource::class.java
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package xyz.nulldev.androidcompat.service
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import mu.KotlinLogging
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
/**
|
||||
* Service emulation class
|
||||
*
|
||||
* TODO Possibly handle starting services via bindService
|
||||
*/
|
||||
|
||||
class ServiceSupport {
|
||||
val runningServices = ConcurrentHashMap<String, Service>()
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
fun startService(context: Context, intent: Intent) {
|
||||
val name = intentToClassName(intent)
|
||||
|
||||
logger.debug { "Starting service: $name" }
|
||||
|
||||
val service = serviceInstanceFromClass(name)
|
||||
|
||||
runningServices[name] = service
|
||||
|
||||
//Setup service
|
||||
thread {
|
||||
callOnCreate(service)
|
||||
//TODO Handle more complex cases
|
||||
service.onStartCommand(intent, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
fun stopService(context: Context, intent: Intent) {
|
||||
val name = intentToClassName(intent)
|
||||
stopService(name)
|
||||
}
|
||||
|
||||
fun stopService(name: String) {
|
||||
logger.debug { "Stopping service: $name" }
|
||||
val service = runningServices.remove(name)
|
||||
if(service == null) {
|
||||
logger.warn { "An attempt was made to stop a service that is not running: $name" }
|
||||
} else {
|
||||
thread {
|
||||
service.onDestroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun stopSelf(service: Service) {
|
||||
stopService(service.javaClass.name)
|
||||
}
|
||||
|
||||
fun callOnCreate(service: Service) = service.onCreate()
|
||||
|
||||
fun intentToClassName(intent: Intent) = intent.component.className!!
|
||||
|
||||
fun serviceInstanceFromClass(className: String): Service {
|
||||
val clazzObj = Class.forName(className)
|
||||
return clazzObj.getDeclaredConstructor().newInstance() as? Service
|
||||
?: throw IllegalArgumentException("$className is not a Service!")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package xyz.nulldev.androidcompat.util
|
||||
|
||||
import android.content.Context
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import xyz.nulldev.androidcompat.androidimpl.CustomContext
|
||||
import xyz.nulldev.androidcompat.androidimpl.FakePackageManager
|
||||
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl
|
||||
import xyz.nulldev.androidcompat.io.AndroidFiles
|
||||
import xyz.nulldev.androidcompat.pm.PackageController
|
||||
import xyz.nulldev.androidcompat.service.ServiceSupport
|
||||
|
||||
/**
|
||||
* Helper class to allow access to Kodein from Java
|
||||
*/
|
||||
object KodeinGlobalHelper {
|
||||
/**
|
||||
* Get the Kodein object
|
||||
*/
|
||||
@JvmStatic
|
||||
fun kodein() = DI.global
|
||||
|
||||
/**
|
||||
* Get a dependency
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <T : Any> instance(type: Class<T>, kodein: DI? = null): T {
|
||||
return when(type) {
|
||||
AndroidFiles::class.java -> {
|
||||
val instance: AndroidFiles by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
ApplicationInfoImpl::class.java -> {
|
||||
val instance: ApplicationInfoImpl by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
ServiceSupport::class.java -> {
|
||||
val instance: ServiceSupport by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
FakePackageManager::class.java -> {
|
||||
val instance: FakePackageManager by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
PackageController::class.java -> {
|
||||
val instance: PackageController by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
CustomContext::class.java -> {
|
||||
val instance: CustomContext by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
Context::class.java -> {
|
||||
val instance: Context by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
else -> throw IllegalArgumentException("Kodein instance not found")
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun <T : Any> instance(type: Class<T>): T {
|
||||
return instance(type, null)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2016 Andy Bao
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package xyz.nulldev.androidcompat.util
|
||||
|
||||
import android.net.Uri
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
||||
/**
|
||||
* Utilites to convert between Java and Android Uris.
|
||||
*/
|
||||
fun Uri.java() = URI(this.toString())
|
||||
fun Uri.file() = File(this.path)
|
||||
fun URI.android() = Uri.parse(this.toString())!!
|
||||
Reference in New Issue
Block a user