From debd1d7d545dea1ffaf9545105eb2e7b3755afd8 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 24 Sep 2019 03:08:48 -0400 Subject: [PATCH 01/50] Update canary channel links --- .../java/com/topjohnwu/magisk/data/network/GithubServices.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/data/network/GithubServices.kt b/app/src/main/java/com/topjohnwu/magisk/data/network/GithubServices.kt index 04cbadcfe..1a10b592f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/data/network/GithubServices.kt +++ b/app/src/main/java/com/topjohnwu/magisk/data/network/GithubServices.kt @@ -19,10 +19,10 @@ interface GithubRawServices { @GET("$MAGISK_FILES/master/beta.json") fun fetchBetaUpdate(): Single - @GET("$MAGISK_FILES/master/canary_builds/release.json") + @GET("$MAGISK_FILES/canary/release.json") fun fetchCanaryUpdate(): Single - @GET("$MAGISK_FILES/master/canary_builds/canary.json") + @GET("$MAGISK_FILES/canary/debug.json") fun fetchCanaryDebugUpdate(): Single @GET From 947dae4900d97500558929e3381c3d20e99a6144 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 25 Sep 2019 23:55:39 -0400 Subject: [PATCH 02/50] Rename classes and small adjustments --- native/jni/init/getinfo.cpp | 2 +- native/jni/magiskboot/bootimg.cpp | 2 +- native/jni/magiskboot/compress.cpp | 4 +- native/jni/magiskboot/compress.h | 2 +- native/jni/magiskboot/ramdisk.cpp | 8 +-- native/jni/magiskhide/hide_policy.cpp | 2 +- native/jni/magiskhide/hide_utils.cpp | 4 +- native/jni/magiskhide/proc_monitor.cpp | 4 +- native/jni/su/su_daemon.cpp | 4 +- .../{BlockingQueue.h => blocking_queue.h} | 58 +++++++++---------- native/jni/utils/include/cpio.h | 2 +- .../utils/include/{OutStream.h => stream.h} | 4 +- native/jni/utils/misc.h | 14 ++--- 13 files changed, 53 insertions(+), 57 deletions(-) rename native/jni/utils/include/{BlockingQueue.h => blocking_queue.h} (55%) rename native/jni/utils/include/{OutStream.h => stream.h} (93%) diff --git a/native/jni/init/getinfo.cpp b/native/jni/init/getinfo.cpp index 285d0896a..904aed527 100644 --- a/native/jni/init/getinfo.cpp +++ b/native/jni/init/getinfo.cpp @@ -67,7 +67,7 @@ static bool check_key_combo() { if (events.empty()) return false; - RunFinally fin([&]() -> void { + run_finally fin([&]() -> void { for (const int &fd : events) close(fd); }); diff --git a/native/jni/magiskboot/bootimg.cpp b/native/jni/magiskboot/bootimg.cpp index 117413a21..4af6c7051 100644 --- a/native/jni/magiskboot/bootimg.cpp +++ b/native/jni/magiskboot/bootimg.cpp @@ -22,7 +22,7 @@ uint32_t dyn_img_hdr::j32 = 0; uint64_t dyn_img_hdr::j64 = 0; static int64_t one_step(unique_ptr &&ptr, int fd, const void *in, size_t size) { - ptr->set_out(make_unique(fd)); + ptr->setOut(make_unique(fd)); if (!ptr->write(in, size)) return -1; return ptr->finalize(); diff --git a/native/jni/magiskboot/compress.cpp b/native/jni/magiskboot/compress.cpp index bd7e41d93..4f37ac591 100644 --- a/native/jni/magiskboot/compress.cpp +++ b/native/jni/magiskboot/compress.cpp @@ -62,7 +62,7 @@ void decompress(char *infile, const char *outfile) { out_fd = strcmp(outfile, "-") == 0 ? STDOUT_FILENO : xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); - cmp->set_out(make_unique(out_fd)); + cmp->setOut(make_unique(out_fd)); if (ext) *ext = '.'; } if (!cmp->write(buf, len)) @@ -108,7 +108,7 @@ void compress(const char *method, const char *infile, const char *outfile) { STDOUT_FILENO : xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); } - cmp->set_out(make_unique(out_fd)); + cmp->setOut(make_unique(out_fd)); read_file(in_file, [&](void *buf, size_t len) -> void { if (!cmp->write(buf, len)) diff --git a/native/jni/magiskboot/compress.h b/native/jni/magiskboot/compress.h index 2b39b315e..81b9bead8 100644 --- a/native/jni/magiskboot/compress.h +++ b/native/jni/magiskboot/compress.h @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include "format.h" diff --git a/native/jni/magiskboot/ramdisk.cpp b/native/jni/magiskboot/ramdisk.cpp index c42c83671..e0898118e 100644 --- a/native/jni/magiskboot/ramdisk.cpp +++ b/native/jni/magiskboot/ramdisk.cpp @@ -246,13 +246,13 @@ void magisk_cpio::compress() { fprintf(stderr, "Compressing cpio -> [%s]\n", RAMDISK_XZ); auto init = entries.extract("init"); XZEncoder encoder; - encoder.set_out(make_unique()); + encoder.setOut(make_unique()); output(encoder); encoder.finalize(); entries.clear(); entries.insert(std::move(init)); auto xz = new cpio_entry(RAMDISK_XZ, S_IFREG); - static_cast(encoder.get_out())->release(xz->data, xz->filesize); + static_cast(encoder.getOut())->release(xz->data, xz->filesize); insert(xz); } @@ -262,13 +262,13 @@ void magisk_cpio::decompress() { return; fprintf(stderr, "Decompressing cpio [%s]\n", RAMDISK_XZ); LZMADecoder decoder; - decoder.set_out(make_unique()); + decoder.setOut(make_unique()); decoder.write(it->second->data, it->second->filesize); decoder.finalize(); entries.erase(it); char *buf; size_t sz; - static_cast(decoder.get_out())->getbuf(buf, sz); + static_cast(decoder.getOut())->getbuf(buf, sz); load_cpio(buf, sz); } diff --git a/native/jni/magiskhide/hide_policy.cpp b/native/jni/magiskhide/hide_policy.cpp index 0ef62f76f..0a20cd75d 100644 --- a/native/jni/magiskhide/hide_policy.cpp +++ b/native/jni/magiskhide/hide_policy.cpp @@ -36,7 +36,7 @@ static inline void lazy_unmount(const char* mountpoint) { } void hide_daemon(int pid) { - RunFinally fin([=]() -> void { + run_finally fin([=]() -> void { // Send resume signal kill(pid, SIGCONT); _exit(0); diff --git a/native/jni/magiskhide/hide_utils.cpp b/native/jni/magiskhide/hide_utils.cpp index 66652a764..e12b75780 100644 --- a/native/jni/magiskhide/hide_utils.cpp +++ b/native/jni/magiskhide/hide_utils.cpp @@ -98,7 +98,7 @@ static int add_list(const char *pkg, const char *proc = "") { // Critical region { - MutexGuard lock(monitor_lock); + mutex_guard lock(monitor_lock); hide_set.emplace(pkg, proc); } @@ -119,7 +119,7 @@ int add_list(int client) { static int rm_list(const char *pkg, const char *proc = "") { { // Critical region - MutexGuard lock(monitor_lock); + mutex_guard lock(monitor_lock); bool remove = false; auto next = hide_set.begin(); decltype(next) cur; diff --git a/native/jni/magiskhide/proc_monitor.cpp b/native/jni/magiskhide/proc_monitor.cpp index a55c29090..28da53872 100644 --- a/native/jni/magiskhide/proc_monitor.cpp +++ b/native/jni/magiskhide/proc_monitor.cpp @@ -76,7 +76,7 @@ static inline long xptrace(int request, pid_t pid, void *addr = nullptr, intptr_ } void update_uid_map() { - MutexGuard lock(monitor_lock); + mutex_guard lock(monitor_lock); uid_proc_map.clear(); string data_path(APP_DATA_DIR); data_path += "/0/"; @@ -336,7 +336,7 @@ void proc_monitor() { continue; } bool detach = false; - RunFinally detach_task([&] { + run_finally f([&] { if (detach) // Non of our business now detach_pid(pid); diff --git a/native/jni/su/su_daemon.cpp b/native/jni/su/su_daemon.cpp index 47fb48de2..1ce07393c 100644 --- a/native/jni/su/su_daemon.cpp +++ b/native/jni/su/su_daemon.cpp @@ -89,7 +89,7 @@ static shared_ptr get_su_info(unsigned uid) { shared_ptr info; { - MutexGuard lock(cache_lock); + mutex_guard lock(cache_lock); if (!cached || cached->uid != uid || !cached->is_fresh()) cached = make_shared(uid); cached->refresh(); @@ -97,7 +97,7 @@ static shared_ptr get_su_info(unsigned uid) { } info->lock(); - RunFinally unlock([&] { + run_finally unlock([&] { info->unlock(); }); diff --git a/native/jni/utils/include/BlockingQueue.h b/native/jni/utils/include/blocking_queue.h similarity index 55% rename from native/jni/utils/include/BlockingQueue.h rename to native/jni/utils/include/blocking_queue.h index b12a67073..f907da1f3 100644 --- a/native/jni/utils/include/BlockingQueue.h +++ b/native/jni/utils/include/blocking_queue.h @@ -2,41 +2,41 @@ #include #include +#include template -class BlockingQueue { +class blocking_queue { std::deque deque{}; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; bool cancelled = false; public: - ~BlockingQueue(); + ~blocking_queue(); T take(); - T &front(); - T &back(); - void put(const T&); - void put(T&&); + T &front() const; + T &back() const; + void push(const T&); + void push(T&&); template< class... Args > - void emplace_back(Args&&... args); + void emplace(Args &&... args); void clear(); void cancel(); }; #define run_and_notify(block) \ -pthread_mutex_lock(&this->lock); \ +mutex_guard g(this->lock); \ block \ -pthread_cond_signal(&this->cond); \ -pthread_mutex_unlock(&this->lock); +pthread_cond_signal(&this->cond); template -BlockingQueue::~BlockingQueue() { +blocking_queue::~blocking_queue() { pthread_mutex_destroy(&lock); pthread_cond_destroy(&cond); } template -T BlockingQueue::take() { - pthread_mutex_lock(&lock); +T blocking_queue::take() { + mutex_guard g(lock); cancelled = false; while (deque.empty() && !cancelled) pthread_cond_wait(&cond, &lock); @@ -44,55 +44,51 @@ T BlockingQueue::take() { pthread_exit(nullptr); T ret(std::move(deque.front())); deque.pop_front(); - pthread_mutex_unlock(&lock); return ret; } template -void BlockingQueue::put(const T &s) { +void blocking_queue::push(const T &s) { run_and_notify({ deque.push_back(s); }) } template -void BlockingQueue::put(T &&s) { +void blocking_queue::push(T &&s) { run_and_notify({ deque.push_back(std::move(s)); }) } template -T &BlockingQueue::front() { - pthread_mutex_lock(&lock); - auto &ret = deque.front(); - pthread_mutex_unlock(&lock); - return ret; +T &blocking_queue::front() const { + mutex_guard g(lock); + return deque.front(); } template -T &BlockingQueue::back() { - pthread_mutex_lock(&lock); - auto &ret = deque.back(); - pthread_mutex_unlock(&lock); - return ret; +T &blocking_queue::back() const { + mutex_guard g(lock); + return deque.back(); } template template -void BlockingQueue::emplace_back(Args &&... args) { +void blocking_queue::emplace(Args &&... args) { run_and_notify({ deque.emplace_back(std::forward(args)...); }) } template -void BlockingQueue::clear() { - pthread_mutex_lock(&lock); +void blocking_queue::clear() { + mutex_guard g(lock); std::deque t; deque.swap(t); - pthread_mutex_unlock(&lock); } template -void BlockingQueue::cancel() { +void blocking_queue::cancel() { run_and_notify({ cancelled = true; std::deque t; deque.swap(t); }) } + +#undef run_and_notify diff --git a/native/jni/utils/include/cpio.h b/native/jni/utils/include/cpio.h index 7ffa169d5..4f6f09917 100644 --- a/native/jni/utils/include/cpio.h +++ b/native/jni/utils/include/cpio.h @@ -6,7 +6,7 @@ #include #include -#include +#include struct cpio_newc_header; diff --git a/native/jni/utils/include/OutStream.h b/native/jni/utils/include/stream.h similarity index 93% rename from native/jni/utils/include/OutStream.h rename to native/jni/utils/include/stream.h index bcb3925b9..5216e75e2 100644 --- a/native/jni/utils/include/OutStream.h +++ b/native/jni/utils/include/stream.h @@ -19,9 +19,9 @@ public: FilterOutStream(strm_ptr &&ptr) : out(std::move(ptr)) {} - void set_out(strm_ptr &&ptr) { out = std::move(ptr); } + void setOut(strm_ptr &&ptr) { out = std::move(ptr); } - OutStream *get_out() { return out.get(); } + OutStream *getOut() { return out.get(); } bool write(const void *buf, size_t len) override { return out ? out->write(buf, len) : false; diff --git a/native/jni/utils/misc.h b/native/jni/utils/misc.h index ad299a7b0..ad6616431 100644 --- a/native/jni/utils/misc.h +++ b/native/jni/utils/misc.h @@ -27,17 +27,17 @@ void gen_rand_str(char *buf, int len, bool varlen = true); #define str_contains(s, ss) ((ss) != nullptr && (s).find(ss) != std::string::npos) #define str_starts(s, ss) ((ss) != nullptr && (s).compare(0, strlen(ss), ss) == 0) -class MutexGuard { +class mutex_guard { public: - explicit MutexGuard(pthread_mutex_t &m): mutex(&m) { + explicit mutex_guard(pthread_mutex_t &m): mutex(&m) { pthread_mutex_lock(mutex); } - explicit MutexGuard(pthread_mutex_t *m): mutex(m) { + explicit mutex_guard(pthread_mutex_t *m): mutex(m) { pthread_mutex_lock(mutex); } - ~MutexGuard() { + ~mutex_guard() { pthread_mutex_unlock(mutex); } @@ -45,13 +45,13 @@ private: pthread_mutex_t *mutex; }; -class RunFinally { +class run_finally { public: - explicit RunFinally(std::function &&fn): fn(std::move(fn)) {} + explicit run_finally(std::function &&fn): fn(std::move(fn)) {} void disable() { fn = nullptr; } - ~RunFinally() { if (fn) fn(); } + ~run_finally() { if (fn) fn(); } private: std::function fn; From b763b81f56373f7ba1574cbaca467f33eaa628f0 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 26 Sep 2019 01:49:50 -0400 Subject: [PATCH 03/50] Use mutex_guard to lock su_info --- native/jni/su/su.h | 4 ++-- native/jni/su/su_daemon.cpp | 13 +++---------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/native/jni/su/su.h b/native/jni/su/su.h index 917b4a011..c496e5bbf 100644 --- a/native/jni/su/su.h +++ b/native/jni/su/su.h @@ -5,6 +5,7 @@ #include #include +#include #define DEFAULT_SHELL "/system/bin/sh" @@ -29,8 +30,7 @@ public: su_info(unsigned uid = 0); ~su_info(); - void lock(); - void unlock(); + mutex_guard lock(); bool is_fresh(); void refresh(); diff --git a/native/jni/su/su_daemon.cpp b/native/jni/su/su_daemon.cpp index 1ce07393c..5debb5cd5 100644 --- a/native/jni/su/su_daemon.cpp +++ b/native/jni/su/su_daemon.cpp @@ -33,12 +33,8 @@ su_info::~su_info() { pthread_mutex_destroy(&_lock); } -void su_info::lock() { - pthread_mutex_lock(&_lock); -} - -void su_info::unlock() { - pthread_mutex_unlock(&_lock); +mutex_guard su_info::lock() { + return mutex_guard(_lock); } bool su_info::is_fresh() { @@ -96,10 +92,7 @@ static shared_ptr get_su_info(unsigned uid) { info = cached; } - info->lock(); - run_finally unlock([&] { - info->unlock(); - }); + auto g = info->lock(); if (info->access.policy == QUERY) { // Not cached, get data from database From 3d4081d0af15af37a79a74151f9f7ffb0c8d8b6b Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 26 Sep 2019 03:14:56 -0400 Subject: [PATCH 04/50] Fix patch verity and forceencrypt --- native/jni/magiskboot/magiskboot.h | 4 +-- native/jni/magiskboot/pattern.cpp | 49 +++++++++++++++--------------- native/jni/magiskboot/ramdisk.cpp | 8 ++--- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/native/jni/magiskboot/magiskboot.h b/native/jni/magiskboot/magiskboot.h index f51c7e8e3..59bab4439 100644 --- a/native/jni/magiskboot/magiskboot.h +++ b/native/jni/magiskboot/magiskboot.h @@ -18,6 +18,6 @@ int hexpatch(const char *image, const char *from, const char *to); int cpio_commands(int argc, char *argv[]); int dtb_commands(int argc, char *argv[]); -char *patch_verity(const void *buf, uint32_t &size); -void patch_encryption(void **buf, uint32_t *size); +char *patch_verity(const void *buf, uint32_t &size, bool inplace = false); +void patch_encryption(void *&buf, uint32_t &size); bool check_env(const char *name); diff --git a/native/jni/magiskboot/pattern.cpp b/native/jni/magiskboot/pattern.cpp index 0630b4794..5df10356d 100644 --- a/native/jni/magiskboot/pattern.cpp +++ b/native/jni/magiskboot/pattern.cpp @@ -22,20 +22,20 @@ static int check_verity_pattern(const char *s) { } static int check_encryption_pattern(const char *s) { - const char *encrypt_list[] = { "forceencrypt", "forcefdeorfbe", nullptr }; - for (int i = 0 ; encrypt_list[i]; ++i) { - int len = strlen(encrypt_list[i]); - if (strncmp(s, encrypt_list[i], len) == 0) + static const char *encrypt_list[] = { "forceencrypt", "forcefdeorfbe" }; + for (auto enc : encrypt_list) { + int len = strlen(enc); + if (strncmp(s, enc, len) == 0) return len; } return -1; } -char *patch_verity(const void *buf, uint32_t &size) { +char *patch_verity(const void *buf, uint32_t &size, bool inplace) { auto src = static_cast(buf); int src_size = size; bool found = false; - char patched[4096]; + auto patched = (char *)(inplace ? buf : xmalloc(size)); int write = 0; for (int read = 0; read < src_size; ++read, ++write) { if (int skip; (skip = check_verity_pattern(src + read)) > 0) { @@ -47,24 +47,25 @@ char *patch_verity(const void *buf, uint32_t &size) { patched[write] = src[read]; } patched[write] = '\0'; - return found ? strdup(patched) : nullptr; -} - -void patch_encryption(void **buf, uint32_t *size) { - int skip, src_size = *size; - char *src = (char *) *buf, *patched = (char *) xcalloc(src_size, 1); - for (int read = 0, write = 0; read < src_size; ++read, ++write) { - if ((skip = check_encryption_pattern(src + read)) > 0) { - fprintf(stderr, "Replace pattern [%.*s] with [encryptable]\n", skip, src + read); - memcpy(patched + read, "encryptable", 11); - read += skip; - write += 11; - *size -= (skip - 11); - } - patched[write] = src[read]; + if (!found) { + if (!inplace) + free(patched); + return nullptr; } - free(*buf); - *buf = patched; + return patched; } - +void patch_encryption(void *&buf, uint32_t &size) { + auto src = static_cast(buf); + int src_size = size; + int write = 0; + for (int read = 0; read < src_size; ++read, ++write) { + if (int skip; (skip = check_encryption_pattern(src + read)) > 0) { + fprintf(stderr, "Found pattern [%.*s]\n", skip, src + read); + size -= skip; + read += skip; + } + src[write] = src[read]; + } + src[write] = '\0'; +} diff --git a/native/jni/magiskboot/ramdisk.cpp b/native/jni/magiskboot/ramdisk.cpp index e0898118e..157ae5ac4 100644 --- a/native/jni/magiskboot/ramdisk.cpp +++ b/native/jni/magiskboot/ramdisk.cpp @@ -52,11 +52,7 @@ void magisk_cpio::patch() { if (!keepverity) { if (fstab) { fprintf(stderr, "Found fstab file [%s]\n", cur->first.data()); - auto buf = patch_verity(cur->second->data, cur->second->filesize); - if (buf) { - free(cur->second->data); - cur->second->data = buf; - } + patch_verity(cur->second->data, cur->second->filesize, true); } else if (cur->first == "verity_key") { rm(cur); continue; @@ -64,7 +60,7 @@ void magisk_cpio::patch() { } if (!keepforceencrypt) { if (fstab) { - patch_encryption(&cur->second->data, &cur->second->filesize); + patch_encryption(cur->second->data, cur->second->filesize); } } } From 578a50b464d0ef9668b1998db89b58a97583cd2d Mon Sep 17 00:00:00 2001 From: Viktor De Pasquale Date: Tue, 24 Sep 2019 16:05:31 +0200 Subject: [PATCH 05/50] Added hiding actions on notifications typed "Download" --- .../topjohnwu/magisk/extensions/XAndroid.kt | 2 ++ .../magisk/model/download/DownloadService.kt | 23 +++++++++++++++---- .../model/download/NotificationService.kt | 3 ++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt index abf5e81c9..2ebb2eb38 100644 --- a/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt @@ -119,3 +119,5 @@ fun ApplicationInfo.getLabel(pm: PackageManager): String { return loadLabel(pm).toString() } + +fun Intent.exists(packageManager: PackageManager) = resolveActivity(packageManager) != null \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt index 4e2c2792e..4fbf8c456 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt @@ -10,6 +10,7 @@ import androidx.core.app.NotificationCompat import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.R import com.topjohnwu.magisk.extensions.chooser +import com.topjohnwu.magisk.extensions.exists import com.topjohnwu.magisk.extensions.provide import com.topjohnwu.magisk.model.entity.internal.Configuration.* import com.topjohnwu.magisk.model.entity.internal.Configuration.Flash.Secondary @@ -17,6 +18,7 @@ import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.* import com.topjohnwu.magisk.ui.flash.FlashActivity import com.topjohnwu.magisk.utils.APKInstall +import org.koin.core.get import java.io.File import kotlin.random.Random.Default.nextInt @@ -76,8 +78,14 @@ open class DownloadService : RemoteFileService() { private fun NotificationCompat.Builder.addActionsInternal(subject: Magisk) = when (val conf = subject.configuration) { - Download -> addAction(0, R.string.download_open_parent, fileIntent(subject.file.parentFile!!)) - .addAction(0, R.string.download_open_self, fileIntent(subject.file)) + Download -> this.apply { + fileIntent(subject.file.parentFile!!) + .takeIf { it.exists(get()) } + ?.let { addAction(0, R.string.download_open_parent, it.chooser()) } + fileIntent(subject.file) + .takeIf { it.exists(get()) } + ?.let { addAction(0, R.string.download_open_self, it.chooser()) } + } Uninstall -> setContentIntent(FlashActivity.uninstallIntent(context, subject.file)) is Flash -> setContentIntent(FlashActivity.flashIntent(context, subject.file, conf is Secondary)) is Patch -> setContentIntent(FlashActivity.patchIntent(context, subject.file, conf.fileUri)) @@ -86,8 +94,14 @@ open class DownloadService : RemoteFileService() { private fun NotificationCompat.Builder.addActionsInternal(subject: Module) = when (subject.configuration) { - Download -> addAction(0, R.string.download_open_parent, fileIntent(subject.file.parentFile!!)) - .addAction(0, R.string.download_open_self, fileIntent(subject.file)) + Download -> this.apply { + fileIntent(subject.file.parentFile!!) + .takeIf { it.exists(get()) } + ?.let { addAction(0, R.string.download_open_parent, it.chooser()) } + fileIntent(subject.file) + .takeIf { it.exists(get()) } + ?.let { addAction(0, R.string.download_open_self, it.chooser()) } + } is Flash -> setContentIntent(FlashActivity.installIntent(context, subject.file)) else -> this } @@ -115,7 +129,6 @@ open class DownloadService : RemoteFileService() { .setDataAndType(file.provide(this), file.type) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .chooser() } class Builder { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/NotificationService.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/NotificationService.kt index 41d8eede0..716869fc3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/NotificationService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/NotificationService.kt @@ -6,10 +6,11 @@ import android.content.Intent import android.os.IBinder import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import org.koin.core.KoinComponent import java.util.* import kotlin.random.Random.Default.nextInt -abstract class NotificationService : Service() { +abstract class NotificationService : Service(), KoinComponent { abstract val defaultNotification: NotificationCompat.Builder From 544bb7459cd43431f3c64437b048be2a8ffe4be3 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 26 Sep 2019 03:49:05 -0400 Subject: [PATCH 06/50] Don't pass by reference --- native/jni/magiskboot/magiskboot.h | 2 +- native/jni/magiskboot/pattern.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/native/jni/magiskboot/magiskboot.h b/native/jni/magiskboot/magiskboot.h index 59bab4439..1cbbd82ea 100644 --- a/native/jni/magiskboot/magiskboot.h +++ b/native/jni/magiskboot/magiskboot.h @@ -19,5 +19,5 @@ int cpio_commands(int argc, char *argv[]); int dtb_commands(int argc, char *argv[]); char *patch_verity(const void *buf, uint32_t &size, bool inplace = false); -void patch_encryption(void *&buf, uint32_t &size); +void patch_encryption(void *buf, uint32_t &size); bool check_env(const char *name); diff --git a/native/jni/magiskboot/pattern.cpp b/native/jni/magiskboot/pattern.cpp index 5df10356d..03bde4879 100644 --- a/native/jni/magiskboot/pattern.cpp +++ b/native/jni/magiskboot/pattern.cpp @@ -55,7 +55,7 @@ char *patch_verity(const void *buf, uint32_t &size, bool inplace) { return patched; } -void patch_encryption(void *&buf, uint32_t &size) { +void patch_encryption(void *buf, uint32_t &size) { auto src = static_cast(buf); int src_size = size; int write = 0; From 64113a69b41e806478b8e135ccfd8abcffd33dbb Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 26 Sep 2019 13:54:40 -0400 Subject: [PATCH 07/50] Remove unused warnings --- native/jni/utils/logging.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/jni/utils/logging.cpp b/native/jni/utils/logging.cpp index 7b3a2c14d..def930461 100644 --- a/native/jni/utils/logging.cpp +++ b/native/jni/utils/logging.cpp @@ -29,7 +29,7 @@ void no_logging() { #define LOG_TAG "Magisk" -static int log_d(const char *fmt, va_list ap) { +[[maybe_unused]] static int log_d(const char *fmt, va_list ap) { return __android_log_vprint(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ap); } From 0cb90e2e55da66dc150d0c897885dc49fb0408dc Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 27 Sep 2019 19:54:03 -0400 Subject: [PATCH 08/50] Update BasePreferenceFragment --- .../magisk/ui/base/BasePreferenceFragment.kt | 46 ++++++++----------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/BasePreferenceFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/BasePreferenceFragment.kt index 2642494cd..9747903a7 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/BasePreferenceFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/BasePreferenceFragment.kt @@ -2,16 +2,12 @@ package com.topjohnwu.magisk.ui.base import android.annotation.SuppressLint import android.content.SharedPreferences -import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.view.children -import androidx.core.view.isVisible import androidx.preference.* import androidx.recyclerview.widget.RecyclerView -import com.topjohnwu.magisk.R import org.koin.android.ext.android.inject abstract class BasePreferenceFragment : PreferenceFragmentCompat(), @@ -35,28 +31,26 @@ abstract class BasePreferenceFragment : PreferenceFragmentCompat(), super.onDestroyView() } - override fun onCreateAdapter(preferenceScreen: PreferenceScreen): RecyclerView.Adapter<*> { - return object : PreferenceGroupAdapter(preferenceScreen) { - @SuppressLint("RestrictedApi") - override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) { - super.onBindViewHolder(holder, position) - when (val preference = getItem(position)) { - is PreferenceCategory -> setZeroPaddingToLayoutChildren(holder.itemView) - else -> holder.itemView.findViewById(R.id.icon_frame)?.isVisible = - preference.icon != null + private fun setAllPreferencesToAvoidHavingExtraSpace(preference: Preference) { + preference.isIconSpaceReserved = false + if (preference is PreferenceGroup) + for (i in 0 until preference.preferenceCount) + setAllPreferencesToAvoidHavingExtraSpace(preference.getPreference(i)) + } + + override fun setPreferenceScreen(preferenceScreen: PreferenceScreen?) { + if (preferenceScreen != null) + setAllPreferencesToAvoidHavingExtraSpace(preferenceScreen) + super.setPreferenceScreen(preferenceScreen) + } + + override fun onCreateAdapter(preferenceScreen: PreferenceScreen?): RecyclerView.Adapter<*> = + object : PreferenceGroupAdapter(preferenceScreen) { + @SuppressLint("RestrictedApi") + override fun onPreferenceHierarchyChange(preference: Preference?) { + if (preference != null) + setAllPreferencesToAvoidHavingExtraSpace(preference) + super.onPreferenceHierarchyChange(preference) } } - } - } - - private fun setZeroPaddingToLayoutChildren(view: View) { - (view as? ViewGroup)?.children?.forEach { - setZeroPaddingToLayoutChildren(it) - } ?: return - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) - view.setPaddingRelative(0, view.paddingTop, view.paddingEnd, view.paddingBottom) - else - view.setPadding(0, view.paddingTop, view.paddingRight, view.paddingBottom) - } } From fc886a5a47d586136457ebf07927c2e1da61dc4c Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 28 Sep 2019 01:56:16 -0400 Subject: [PATCH 09/50] Merge Teanity into sources --- app/build.gradle | 18 +- .../magisk/databinding/AdaptersGeneric.kt | 26 ++ .../magisk/databinding/AdaptersRecycler.kt | 57 ++++ .../magisk/databinding/BindingBoundAdapter.kt | 13 + .../magisk/databinding/RecyclerViewItems.kt | 48 +++ .../topjohnwu/magisk/di/ApplicationModule.kt | 2 +- .../magisk/extensions/DataBinding.kt | 57 ++++ .../com/topjohnwu/magisk/extensions/Dimens.kt | 9 + .../com/topjohnwu/magisk/extensions/Misc.kt | 6 + .../com/topjohnwu/magisk/extensions/RxJava.kt | 201 +++++++++++++ .../topjohnwu/magisk/extensions/Snackbar.kt | 126 ++++++++ .../topjohnwu/magisk/extensions/XAndroid.kt | 34 ++- .../topjohnwu/magisk/extensions/XBinding.kt | 2 +- .../com/topjohnwu/magisk/extensions/XList.kt | 7 +- .../com/topjohnwu/magisk/extensions/XRx.kt | 9 - .../magisk/model/binding/BindingAdapter.kt | 2 +- .../model/download/RemoteFileService.kt | 2 +- .../topjohnwu/magisk/model/entity/Version.kt | 3 - .../model/entity/recycler/HideRvItem.kt | 10 +- .../model/entity/recycler/LenientRvItem.kt | 2 +- .../magisk/model/entity/recycler/LogRvItem.kt | 6 +- .../model/entity/recycler/ModuleRvItem.kt | 6 +- .../model/entity/recycler/PolicyRvItem.kt | 8 +- .../model/entity/recycler/SectionRvItem.kt | 2 +- .../model/entity/recycler/SpinnerRvItem.kt | 2 +- .../magisk/model/events/EventHandler.kt | 18 ++ .../topjohnwu/magisk/model/events/RxEvents.kt | 2 +- .../magisk/model/events/SimpleViewEvent.kt | 7 + .../magisk/model/events/SnackbarEvent.kt | 28 ++ .../magisk/model/events/ViewEventObserver.kt | 17 ++ .../magisk/model/events/ViewEvents.kt | 13 +- .../model/navigation/MagiskNavigationEvent.kt | 6 +- .../com/topjohnwu/magisk/tasks/FlashZip.kt | 2 +- .../topjohnwu/magisk/tasks/MagiskInstaller.kt | 1 - .../com/topjohnwu/magisk/ui/MainActivity.kt | 6 +- .../magisk/ui/base/MagiskActivity.kt | 48 ++- .../magisk/ui/base/MagiskFragment.kt | 47 ++- .../magisk/ui/base/MagiskViewModel.kt | 8 +- .../magisk/ui/flash/FlashViewModel.kt | 9 +- .../topjohnwu/magisk/ui/hide/HideViewModel.kt | 12 +- .../topjohnwu/magisk/ui/home/HomeFragment.kt | 4 +- .../topjohnwu/magisk/ui/home/HomeViewModel.kt | 10 +- .../topjohnwu/magisk/ui/log/LogFragment.kt | 2 +- .../topjohnwu/magisk/ui/log/LogViewModel.kt | 14 +- .../magisk/ui/module/ModuleViewModel.kt | 12 +- .../magisk/ui/module/ModulesFragment.kt | 2 +- .../magisk/ui/module/ReposFragment.kt | 2 +- .../magisk/ui/settings/SettingsFragment.kt | 3 +- .../magisk/ui/superuser/SuperuserViewModel.kt | 12 +- .../magisk/ui/surequest/SuRequestActivity.kt | 2 +- .../magisk/ui/surequest/SuRequestViewModel.kt | 8 +- .../magisk/utils/DataBindingAdapters.kt | 2 +- .../magisk/utils/DiffObservableList.kt | 234 +++++++++++++++ .../topjohnwu/magisk/utils/KItemDecoration.kt | 117 ++++++++ .../magisk/utils/KObservableField.kt | 49 ++++ .../com/topjohnwu/magisk/utils/PatchAPK.kt | 2 +- .../java/com/topjohnwu/magisk/utils/RxBus.kt | 36 +++ .../com/topjohnwu/magisk/view/MagiskDialog.kt | 155 ---------- .../topjohnwu/magisk/view/MarkDownWindow.kt | 2 +- .../magisk/view/dialogs/EnvFixDialog.kt | 2 +- .../magisk/viewmodel/LoadingViewModel.kt | 78 +++++ .../magisk/viewmodel/ObservableViewModel.kt | 46 +++ .../magisk/viewmodel/StatefulViewModel.kt | 15 + .../magisk/viewmodel/TeanityViewModel.kt | 33 +++ .../main/res/layout/dialog_magisk_base.xml | 276 ------------------ app/src/main/res/layout/fragment_magisk.xml | 2 +- build.gradle | 4 +- 67 files changed, 1436 insertions(+), 570 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/databinding/AdaptersGeneric.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/databinding/AdaptersRecycler.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/databinding/BindingBoundAdapter.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/databinding/RecyclerViewItems.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/extensions/DataBinding.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/extensions/Dimens.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/extensions/Misc.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/extensions/RxJava.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/extensions/Snackbar.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/extensions/XRx.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/model/entity/Version.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/events/EventHandler.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/events/SimpleViewEvent.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/events/ViewEventObserver.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/DiffObservableList.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/KItemDecoration.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/KObservableField.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/RxBus.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/viewmodel/LoadingViewModel.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/viewmodel/ObservableViewModel.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/viewmodel/StatefulViewModel.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/viewmodel/TeanityViewModel.kt delete mode 100644 app/src/main/res/layout/dialog_magisk_base.xml diff --git a/app/build.gradle b/app/build.gradle index 4b1156ed7..348df90e3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -60,9 +60,19 @@ dependencies { implementation 'com.github.topjohnwu:jtar:1.0.0' implementation 'com.jakewharton.timber:timber:4.7.1' - implementation 'com.github.skoumalcz:teanity:0.3.3' implementation 'com.ncapdevi:frag-nav:3.2.0' implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.6' + implementation 'com.karumi:dexter:6.0.0' + + implementation "io.reactivex.rxjava2:rxjava:2.2.12" + implementation "io.reactivex.rxjava2:rxkotlin:2.4.0" + + implementation "org.jetbrains.kotlin:kotlin-stdlib:${vKotlin}" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${vKotlin}" + + def vBAdapt = '3.1.1' + implementation "me.tatarka.bindingcollectionadapter2:bindingcollectionadapter:${vBAdapt}" + implementation "me.tatarka.bindingcollectionadapter2:bindingcollectionadapter-recyclerview:${vBAdapt}" def vMarkwon = '3.1.0' implementation "ru.noties.markwon:core:${vMarkwon}" @@ -104,8 +114,9 @@ dependencies { implementation "com.github.topjohnwu:room-runtime:${vRoom}" kapt "androidx.room:room-compiler:${vRoom}" - implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlinVer}" - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${kotlinVer}" + def vNav = "2.1.0" + implementation "androidx.navigation:navigation-fragment-ktx:$vNav" + implementation "androidx.navigation:navigation-ui-ktx:$vNav" implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.browser:browser:1.0.0' @@ -115,5 +126,6 @@ dependencies { implementation 'androidx.work:work-runtime:2.2.0' implementation 'androidx.transition:transition:1.2.0-rc01' implementation 'androidx.multidex:multidex:2.0.1' + implementation 'androidx.core:core-ktx:1.1.0' implementation 'com.google.android.material:material:1.1.0-alpha10' } diff --git a/app/src/main/java/com/topjohnwu/magisk/databinding/AdaptersGeneric.kt b/app/src/main/java/com/topjohnwu/magisk/databinding/AdaptersGeneric.kt new file mode 100644 index 000000000..0038cb7af --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/databinding/AdaptersGeneric.kt @@ -0,0 +1,26 @@ +package com.topjohnwu.magisk.databinding + +import android.view.View +import androidx.core.view.isGone +import androidx.core.view.isInvisible +import androidx.databinding.BindingAdapter + +@BindingAdapter("gone") +fun setGone(view: View, gone: Boolean) { + view.isGone = gone +} + +@BindingAdapter("invisible") +fun setInvisible(view: View, invisible: Boolean) { + view.isInvisible = invisible +} + +@BindingAdapter("goneUnless") +fun setGoneUnless(view: View, goneUnless: Boolean) { + setGone(view, goneUnless.not()) +} + +@BindingAdapter("invisibleUnless") +fun setInvisibleUnless(view: View, invisibleUnless: Boolean) { + setInvisible(view, invisibleUnless.not()) +} diff --git a/app/src/main/java/com/topjohnwu/magisk/databinding/AdaptersRecycler.kt b/app/src/main/java/com/topjohnwu/magisk/databinding/AdaptersRecycler.kt new file mode 100644 index 000000000..9815383b3 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/databinding/AdaptersRecycler.kt @@ -0,0 +1,57 @@ +package com.topjohnwu.magisk.databinding + +import android.graphics.drawable.GradientDrawable +import android.graphics.drawable.InsetDrawable +import androidx.databinding.BindingAdapter +import androidx.recyclerview.widget.RecyclerView +import com.topjohnwu.magisk.extensions.startEndToLeftRight +import com.topjohnwu.magisk.extensions.toPx +import com.topjohnwu.magisk.utils.KItemDecoration +import kotlin.math.roundToInt + +@BindingAdapter( + "dividerColor", + "dividerHorizontal", + "dividerSize", + "dividerAfterLast", + "dividerMarginStart", + "dividerMarginEnd", + "dividerMarginTop", + "dividerMarginBottom", + requireAll = false +) +fun setDivider( + view: RecyclerView, + color: Int, + horizontal: Boolean, + _size: Float, + _afterLast: Boolean?, + marginStartF: Float, + marginEndF: Float, + marginTopF: Float, + marginBottomF: Float +) { + val orientation = if (horizontal) RecyclerView.HORIZONTAL else RecyclerView.VERTICAL + val size = if (_size > 0) _size.roundToInt() else 1.toPx() + val (width, height) = if (horizontal) size to 1 else 1 to size + val afterLast = _afterLast ?: true + + val marginStart = marginStartF.roundToInt() + val marginEnd = marginEndF.roundToInt() + val marginTop = marginTopF.roundToInt() + val marginBottom = marginBottomF.roundToInt() + val (marginLeft, marginRight) = view.context.startEndToLeftRight(marginStart, marginEnd) + + val drawable = GradientDrawable().apply { + setSize(width, height) + shape = GradientDrawable.RECTANGLE + setColor(color) + }.let { + InsetDrawable(it, marginLeft, marginTop, marginRight, marginBottom) + } + + val decoration = KItemDecoration(view.context, orientation) + .setDeco(drawable) + .apply { showAfterLast = afterLast } + view.addItemDecoration(decoration) +} diff --git a/app/src/main/java/com/topjohnwu/magisk/databinding/BindingBoundAdapter.kt b/app/src/main/java/com/topjohnwu/magisk/databinding/BindingBoundAdapter.kt new file mode 100644 index 000000000..39d439303 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/databinding/BindingBoundAdapter.kt @@ -0,0 +1,13 @@ +package com.topjohnwu.magisk.databinding + +import androidx.databinding.ViewDataBinding +import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter + +open class BindingBoundAdapter : BindingRecyclerViewAdapter() { + + override fun onBindBinding(binding: ViewDataBinding, variableId: Int, layoutRes: Int, position: Int, item: RvItem) { + super.onBindBinding(binding, variableId, layoutRes, position, item) + + item.onBindingBound(binding) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/databinding/RecyclerViewItems.kt b/app/src/main/java/com/topjohnwu/magisk/databinding/RecyclerViewItems.kt new file mode 100644 index 000000000..087a753f1 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/databinding/RecyclerViewItems.kt @@ -0,0 +1,48 @@ +package com.topjohnwu.magisk.databinding + +import androidx.annotation.CallSuper +import androidx.databinding.ViewDataBinding +import com.topjohnwu.magisk.BR +import com.topjohnwu.magisk.utils.DiffObservableList +import me.tatarka.bindingcollectionadapter2.ItemBinding + +abstract class RvItem { + + abstract val layoutRes: Int + + @CallSuper + open fun bind(binding: ItemBinding<*>) { + binding.set(BR.item, layoutRes) + } + + /** + * This callback is useful if you want to manipulate your views directly. + * If you want to use this callback, you must set [me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter] + * on your RecyclerView and call it from there. You can use [BindingBoundAdapter] for your convenience. + */ + open fun onBindingBound(binding: ViewDataBinding) {} +} + +abstract class ComparableRvItem : RvItem() { + + abstract fun itemSameAs(other: T): Boolean + abstract fun contentSameAs(other: T): Boolean + @Suppress("UNCHECKED_CAST") + open fun genericItemSameAs(other: Any): Boolean = other::class == this::class && itemSameAs(other as T) + @Suppress("UNCHECKED_CAST") + open fun genericContentSameAs(other: Any): Boolean = other::class == this::class && contentSameAs(other as T) + + companion object { + val callback = object : DiffObservableList.Callback> { + override fun areItemsTheSame( + oldItem: ComparableRvItem<*>, + newItem: ComparableRvItem<*> + ) = oldItem.genericItemSameAs(newItem) + + override fun areContentsTheSame( + oldItem: ComparableRvItem<*>, + newItem: ComparableRvItem<*> + ) = oldItem.genericContentSameAs(newItem) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt b/app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt index d03cec643..437c69c3a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt +++ b/app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt @@ -7,7 +7,7 @@ import android.content.Context import android.os.Build import android.os.Bundle import androidx.preference.PreferenceManager -import com.skoumal.teanity.rxbus.RxBus +import com.topjohnwu.magisk.utils.RxBus import org.koin.core.qualifier.named import org.koin.dsl.module diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/DataBinding.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/DataBinding.kt new file mode 100644 index 000000000..f671815a1 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/DataBinding.kt @@ -0,0 +1,57 @@ +package com.topjohnwu.magisk.extensions + +import androidx.databinding.Observable +import androidx.databinding.ObservableBoolean +import androidx.databinding.ObservableField +import androidx.databinding.ObservableInt + +fun ObservableField.addOnPropertyChangedCallback( + removeAfterChanged: Boolean = false, + callback: (T?) -> Unit +) { + addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() { + override fun onPropertyChanged(sender: Observable?, propertyId: Int) { + callback(get()) + if (removeAfterChanged) removeOnPropertyChangedCallback(this) + } + }) +} + +fun ObservableInt.addOnPropertyChangedCallback( + removeAfterChanged: Boolean = false, + callback: (Int) -> Unit +) { + addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() { + override fun onPropertyChanged(sender: Observable?, propertyId: Int) { + callback(get()) + if (removeAfterChanged) removeOnPropertyChangedCallback(this) + } + }) +} + +fun ObservableBoolean.addOnPropertyChangedCallback( + removeAfterChanged: Boolean = false, + callback: (Boolean) -> Unit +) { + addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() { + override fun onPropertyChanged(sender: Observable?, propertyId: Int) { + callback(get()) + if (removeAfterChanged) removeOnPropertyChangedCallback(this) + } + }) +} + +inline fun ObservableField.update(block: (T?) -> Unit) { + set(get().apply(block)) +} + +inline fun ObservableField.updateNonNull(block: (T) -> Unit) { + update { + it ?: return@update + block(it) + } +} + +inline fun ObservableInt.update(block: (Int) -> Unit) { + set(get().apply(block)) +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/Dimens.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/Dimens.kt new file mode 100644 index 000000000..77c5d3439 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/Dimens.kt @@ -0,0 +1,9 @@ +package com.topjohnwu.magisk.extensions + +import android.content.res.Resources +import kotlin.math.ceil +import kotlin.math.roundToInt + +fun Int.toDp(): Int = ceil(this / Resources.getSystem().displayMetrics.density).roundToInt() + +fun Int.toPx(): Int = (this * Resources.getSystem().displayMetrics.density).roundToInt() diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/Misc.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/Misc.kt new file mode 100644 index 000000000..b3a54ab4f --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/Misc.kt @@ -0,0 +1,6 @@ +package com.topjohnwu.magisk.extensions + +import android.os.Handler +import android.os.Looper + +fun ui(body: () -> Unit) = Handler(Looper.getMainLooper()).post(body) \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/RxJava.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/RxJava.kt new file mode 100644 index 000000000..a4eefb0e9 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/RxJava.kt @@ -0,0 +1,201 @@ +package com.topjohnwu.magisk.extensions + +import androidx.databinding.ObservableField +import com.topjohnwu.magisk.utils.KObservableField +import io.reactivex.* +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposables +import io.reactivex.functions.BiFunction +import io.reactivex.schedulers.Schedulers +import androidx.databinding.Observable as BindingObservable + +fun Observable.applySchedulers( + subscribeOn: Scheduler = Schedulers.io(), + observeOn: Scheduler = AndroidSchedulers.mainThread() +): Observable = this.subscribeOn(subscribeOn).observeOn(observeOn) + +fun Flowable.applySchedulers( + subscribeOn: Scheduler = Schedulers.io(), + observeOn: Scheduler = AndroidSchedulers.mainThread() +): Flowable = this.subscribeOn(subscribeOn).observeOn(observeOn) + +fun Single.applySchedulers( + subscribeOn: Scheduler = Schedulers.io(), + observeOn: Scheduler = AndroidSchedulers.mainThread() +): Single = this.subscribeOn(subscribeOn).observeOn(observeOn) + +fun Maybe.applySchedulers( + subscribeOn: Scheduler = Schedulers.io(), + observeOn: Scheduler = AndroidSchedulers.mainThread() +): Maybe = this.subscribeOn(subscribeOn).observeOn(observeOn) + +fun Completable.applySchedulers( + subscribeOn: Scheduler = Schedulers.io(), + observeOn: Scheduler = AndroidSchedulers.mainThread() +): Completable = this.subscribeOn(subscribeOn).observeOn(observeOn) + +/*=== ALIASES FOR OBSERVABLES ===*/ + +typealias OnCompleteListener = () -> Unit +typealias OnSuccessListener = (T) -> Unit +typealias OnErrorListener = (Throwable) -> Unit + +/*=== ALIASES FOR OBSERVABLES ===*/ + +fun Observable.subscribeK( + onError: OnErrorListener = { it.printStackTrace() }, + onComplete: OnCompleteListener = {}, + onNext: OnSuccessListener = {} +) = applySchedulers() + .subscribe(onNext, onError, onComplete) + +fun Single.subscribeK( + onError: OnErrorListener = { it.printStackTrace() }, + onNext: OnSuccessListener = {} +) = applySchedulers() + .subscribe(onNext, onError) + +fun Maybe.subscribeK( + onError: OnErrorListener = { it.printStackTrace() }, + onComplete: OnCompleteListener = {}, + onNext: OnSuccessListener = {} +) = applySchedulers() + .subscribe(onNext, onError, onComplete) + +fun Flowable.subscribeK( + onError: OnErrorListener = { it.printStackTrace() }, + onComplete: OnCompleteListener = {}, + onNext: OnSuccessListener = {} +) = applySchedulers() + .subscribe(onNext, onError, onComplete) + +fun Completable.subscribeK( + onError: OnErrorListener = { it.printStackTrace() }, + onComplete: OnCompleteListener = {} +) = applySchedulers() + .subscribe(onComplete, onError) + + +fun Observable.updateBy( + field: KObservableField +) = doOnNextUi { field.value = it } + .doOnErrorUi { field.value = null } + +fun Single.updateBy( + field: KObservableField +) = doOnSuccessUi { field.value = it } + .doOnErrorUi { field.value = null } + +fun Maybe.updateBy( + field: KObservableField +) = doOnSuccessUi { field.value = it } + .doOnErrorUi { field.value = null } + .doOnComplete { field.value = field.value } + +fun Flowable.updateBy( + field: KObservableField +) = doOnNextUi { field.value = it } + .doOnErrorUi { field.value = null } + +fun Completable.updateBy( + field: KObservableField +) = doOnCompleteUi { field.value = true } + .doOnErrorUi { field.value = false } + + +fun Observable.doOnSubscribeUi(body: () -> Unit) = + doOnSubscribe { ui { body() } } + +fun Single.doOnSubscribeUi(body: () -> Unit) = + doOnSubscribe { ui { body() } } + +fun Maybe.doOnSubscribeUi(body: () -> Unit) = + doOnSubscribe { ui { body() } } + +fun Flowable.doOnSubscribeUi(body: () -> Unit) = + doOnSubscribe { ui { body() } } + +fun Completable.doOnSubscribeUi(body: () -> Unit) = + doOnSubscribe { ui { body() } } + + +fun Observable.doOnErrorUi(body: (Throwable) -> Unit) = + doOnError { ui { body(it) } } + +fun Single.doOnErrorUi(body: (Throwable) -> Unit) = + doOnError { ui { body(it) } } + +fun Maybe.doOnErrorUi(body: (Throwable) -> Unit) = + doOnError { ui { body(it) } } + +fun Flowable.doOnErrorUi(body: (Throwable) -> Unit) = + doOnError { ui { body(it) } } + +fun Completable.doOnErrorUi(body: (Throwable) -> Unit) = + doOnError { ui { body(it) } } + + +fun Observable.doOnNextUi(body: (T) -> Unit) = + doOnNext { ui { body(it) } } + +fun Flowable.doOnNextUi(body: (T) -> Unit) = + doOnNext { ui { body(it) } } + +fun Single.doOnSuccessUi(body: (T) -> Unit) = + doOnSuccess { ui { body(it) } } + +fun Maybe.doOnSuccessUi(body: (T) -> Unit) = + doOnSuccess { ui { body(it) } } + +fun Maybe.doOnCompleteUi(body: () -> Unit) = + doOnComplete { ui { body() } } + +fun Completable.doOnCompleteUi(body: () -> Unit) = + doOnComplete { ui { body() } } + + +fun Observable>.mapList( + transformer: (T) -> R +) = flatMapIterable { it } + .map(transformer) + .toList() + +fun Single>.mapList( + transformer: (T) -> R +) = flattenAsFlowable { it } + .map(transformer) + .toList() + +fun Maybe>.mapList( + transformer: (T) -> R +) = flattenAsFlowable { it } + .map(transformer) + .toList() + +fun Flowable>.mapList( + transformer: (T) -> R +) = flatMapIterable { it } + .map(transformer) + .toList() + +fun ObservableField.toObservable(): Observable { + val observableField = this + return Observable.create { emitter -> + observableField.get()?.let { emitter.onNext(it) } + + val callback = object : BindingObservable.OnPropertyChangedCallback() { + override fun onPropertyChanged(sender: BindingObservable?, propertyId: Int) { + observableField.get()?.let { emitter.onNext(it) } + } + } + observableField.addOnPropertyChangedCallback(callback) + emitter.setDisposable(Disposables.fromAction { + observableField.removeOnPropertyChangedCallback(callback) + }) + } +} + +fun T.toSingle() = Single.just(this) + +fun zip(t1: Single, t2: Single, zipper: (T1, T2) -> R) = + Single.zip(t1, t2, BiFunction { rt1, rt2 -> zipper(rt1, rt2) }) diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/Snackbar.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/Snackbar.kt new file mode 100644 index 000000000..a9e5e133f --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/Snackbar.kt @@ -0,0 +1,126 @@ +package com.topjohnwu.magisk.extensions + +import android.content.Context +import android.content.res.ColorStateList +import android.view.View +import android.widget.TextView +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.annotation.StringRes +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.fragment.app.Fragment +import com.google.android.material.snackbar.Snackbar + +fun AppCompatActivity.snackbar( + view: View, + @StringRes messageRes: Int, + length: Int = Snackbar.LENGTH_SHORT, + f: Snackbar.() -> Unit = {} +) { + snackbar(view, getString(messageRes), length, f) +} + +fun AppCompatActivity.snackbar( + view: View, + message: String, + length: Int = Snackbar.LENGTH_SHORT, + f: Snackbar.() -> Unit = {} +) = Snackbar.make(view, message, length) + .apply(f) + .show() + +fun Fragment.snackbar( + view: View, + @StringRes messageRes: Int, + length: Int = Snackbar.LENGTH_SHORT, + f: Snackbar.() -> Unit = {} +) { + snackbar(view, getString(messageRes), length, f) +} + +fun Fragment.snackbar( + view: View, + message: String, + length: Int = Snackbar.LENGTH_SHORT, + f: Snackbar.() -> Unit = {} +) = Snackbar.make(view, message, length) + .apply(f) + .show() + +fun Snackbar.action(init: KSnackbar.() -> Unit) = apply { + val config = KSnackbar().apply(init) + + setAction(config.title(context), config.onClickListener) + + when { + config.hasValidColor -> setActionTextColor(config.color(context) ?: return@apply) + config.hasValidColorStateList -> setActionTextColor(config.colorStateList(context) ?: return@apply) + } +} + +class KSnackbar { + var colorRes: Int = -1 + var colorStateListRes: Int = -1 + + var title: CharSequence = "" + var titleRes: Int = -1 + + internal var onClickListener: (View) -> Unit = {} + internal val hasValidColor get() = colorRes != -1 + internal val hasValidColorStateList get() = colorStateListRes != -1 + + fun onClicked(listener: (View) -> Unit) { + onClickListener = listener + } + + internal fun title(context: Context) = if (title.isBlank()) context.getString(titleRes) else title + internal fun colorStateList(context: Context) = context.colorStateListCompat(colorStateListRes) + internal fun color(context: Context) = context.colorCompat(colorRes) +} + +@Deprecated("Kotlin DSL version is preferred", ReplaceWith("action {}")) +fun Snackbar.action( + @StringRes actionRes: Int, + @ColorRes colorRes: Int? = null, + listener: (View) -> Unit +) { + view.resources.getString(actionRes) + colorRes?.let { ContextCompat.getColor(view.context, colorRes) } + action {} +} + +@Deprecated("Kotlin DSL version is preferred", ReplaceWith("action {}")) +fun Snackbar.action(action: String, @ColorInt color: Int? = null, listener: (View) -> Unit) { + setAction(action, listener) + color?.let { setActionTextColor(color) } +} + +fun Snackbar.textColorRes(@ColorRes colorRes: Int) { + textColor(context.colorCompat(colorRes) ?: return) +} + +fun Snackbar.textColor(@ColorInt color: Int) { + val tv = view.findViewById(com.google.android.material.R.id.snackbar_text) + tv.setTextColor(color) +} + +fun Snackbar.backgroundColorRes(@ColorRes colorRes: Int) { + backgroundColor(context.colorCompat(colorRes) ?: return) +} + +fun Snackbar.backgroundColor(@ColorInt color: Int) { + ViewCompat.setBackgroundTintList( + view, + ColorStateList.valueOf(color) + ) +} + +fun Snackbar.alert() { + textColor(0xF44336) +} + +fun Snackbar.success() { + textColor(0x4CAF50) +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt index 2ebb2eb38..3c669600a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt @@ -8,9 +8,15 @@ import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.PackageManager.* import android.content.res.Configuration +import android.content.res.Resources import android.database.Cursor import android.net.Uri +import android.os.Build import android.provider.OpenableColumns +import android.view.View +import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes +import androidx.core.content.ContextCompat import com.topjohnwu.magisk.utils.FileProvider import com.topjohnwu.magisk.utils.currentLocale import java.io.File @@ -120,4 +126,30 @@ fun ApplicationInfo.getLabel(pm: PackageManager): String { return loadLabel(pm).toString() } -fun Intent.exists(packageManager: PackageManager) = resolveActivity(packageManager) != null \ No newline at end of file +fun Intent.exists(packageManager: PackageManager) = resolveActivity(packageManager) != null + +fun Context.colorCompat(@ColorRes id: Int) = try { + ContextCompat.getColor(this, id) +} catch (e: Resources.NotFoundException) { + null +} + +fun Context.colorStateListCompat(@ColorRes id: Int) = try { + ContextCompat.getColorStateList(this, id) +} catch (e: Resources.NotFoundException) { + null +} + +fun Context.drawableCompat(@DrawableRes id: Int) = ContextCompat.getDrawable(this, id) +/** + * Pass [start] and [end] dimensions, function will return left and right + * with respect to RTL layout direction + */ +fun Context.startEndToLeftRight(start: Int, end: Int): Pair { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && + resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL + ) { + return end to start + } + return start to end +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/XBinding.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/XBinding.kt index 36874f1e4..c0c5c8dd1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/extensions/XBinding.kt +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/XBinding.kt @@ -1,6 +1,6 @@ package com.topjohnwu.magisk.extensions -import com.skoumal.teanity.util.KObservableField +import com.topjohnwu.magisk.utils.KObservableField fun KObservableField.toggle() { diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/XList.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/XList.kt index ee79f13f8..88e5807c1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/extensions/XList.kt +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/XList.kt @@ -2,8 +2,7 @@ package com.topjohnwu.magisk.extensions import androidx.collection.SparseArrayCompat import androidx.databinding.ObservableList -import com.skoumal.teanity.extensions.subscribeK -import com.skoumal.teanity.util.DiffObservableList +import com.topjohnwu.magisk.utils.DiffObservableList import io.reactivex.disposables.Disposable fun MutableList.update(newList: List) { @@ -26,8 +25,8 @@ fun List.toShellCmd(): String { } fun ObservableList.sendUpdatesTo( - target: DiffObservableList, - mapper: (List) -> List + target: DiffObservableList, + mapper: (List) -> List ) = addOnListChangedCallback(object : ObservableList.OnListChangedCallback>() { override fun onChanged(sender: ObservableList?) { diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/XRx.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/XRx.kt deleted file mode 100644 index 7dbb2bd08..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/extensions/XRx.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.topjohnwu.magisk.extensions - -import io.reactivex.Single -import io.reactivex.functions.BiFunction - -fun T.toSingle() = Single.just(this) - -fun zip(t1: Single, t2: Single, zipper: (T1, T2) -> R) = - Single.zip(t1, t2, BiFunction { rt1, rt2 -> zipper(rt1, rt2) }) \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/binding/BindingAdapter.kt b/app/src/main/java/com/topjohnwu/magisk/model/binding/BindingAdapter.kt index 5bfad68c6..ec4cd43b2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/binding/BindingAdapter.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/binding/BindingAdapter.kt @@ -2,7 +2,7 @@ package com.topjohnwu.magisk.model.binding import androidx.databinding.ViewDataBinding import androidx.recyclerview.widget.RecyclerView -import com.skoumal.teanity.databinding.ComparableRvItem +import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.model.entity.recycler.LenientRvItem import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt index 84b5a6742..1c5fea005 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt @@ -3,11 +3,11 @@ package com.topjohnwu.magisk.model.download import android.app.Activity import android.content.Intent import androidx.core.app.NotificationCompat -import com.skoumal.teanity.extensions.subscribeK import com.topjohnwu.magisk.R import com.topjohnwu.magisk.data.network.GithubRawServices import com.topjohnwu.magisk.di.NullActivity import com.topjohnwu.magisk.extensions.get +import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.writeTo import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.* diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/Version.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/Version.kt deleted file mode 100644 index 5a7728a15..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/Version.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.topjohnwu.magisk.model.entity - -data class Version(val version: String, val versionCode: Int) \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt index 1ea743e92..9e36d401d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt @@ -1,17 +1,17 @@ package com.topjohnwu.magisk.model.entity.recycler -import com.skoumal.teanity.databinding.ComparableRvItem -import com.skoumal.teanity.extensions.addOnPropertyChangedCallback -import com.skoumal.teanity.rxbus.RxBus -import com.skoumal.teanity.util.DiffObservableList -import com.skoumal.teanity.util.KObservableField +import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.databinding.ComparableRvItem +import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback import com.topjohnwu.magisk.extensions.inject import com.topjohnwu.magisk.extensions.toggle import com.topjohnwu.magisk.model.entity.HideAppInfo import com.topjohnwu.magisk.model.entity.HideTarget import com.topjohnwu.magisk.model.entity.state.IndeterminateState import com.topjohnwu.magisk.model.events.HideProcessEvent +import com.topjohnwu.magisk.utils.DiffObservableList +import com.topjohnwu.magisk.utils.KObservableField class HideRvItem(val item: HideAppInfo, targets: List) : ComparableRvItem() { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LenientRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LenientRvItem.kt index ddbd2fbec..f21df6b91 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LenientRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LenientRvItem.kt @@ -2,7 +2,7 @@ package com.topjohnwu.magisk.model.entity.recycler import androidx.databinding.ViewDataBinding import androidx.recyclerview.widget.RecyclerView -import com.skoumal.teanity.databinding.ComparableRvItem +import com.topjohnwu.magisk.databinding.ComparableRvItem /** * This item addresses issues where enclosing recycler has to be invalidated or generally diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt index 8f467b8fd..ad278ef80 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt @@ -1,14 +1,14 @@ package com.topjohnwu.magisk.model.entity.recycler -import com.skoumal.teanity.databinding.ComparableRvItem -import com.skoumal.teanity.util.DiffObservableList -import com.skoumal.teanity.util.KObservableField import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.timeFormatMedium import com.topjohnwu.magisk.extensions.toTime import com.topjohnwu.magisk.extensions.toggle import com.topjohnwu.magisk.model.entity.MagiskLog import com.topjohnwu.magisk.model.entity.WrappedMagiskLog +import com.topjohnwu.magisk.utils.DiffObservableList +import com.topjohnwu.magisk.utils.KObservableField class LogRvItem : ComparableRvItem() { override val layoutRes: Int = R.layout.item_page_log diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/ModuleRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/ModuleRvItem.kt index 57caade72..1af52be7b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/ModuleRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/ModuleRvItem.kt @@ -2,14 +2,14 @@ package com.topjohnwu.magisk.model.entity.recycler import android.content.res.Resources import androidx.annotation.StringRes -import com.skoumal.teanity.databinding.ComparableRvItem -import com.skoumal.teanity.extensions.addOnPropertyChangedCallback -import com.skoumal.teanity.util.KObservableField import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.databinding.ComparableRvItem +import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.toggle import com.topjohnwu.magisk.model.entity.module.Module import com.topjohnwu.magisk.model.entity.module.Repo +import com.topjohnwu.magisk.utils.KObservableField class ModuleRvItem(val item: Module) : ComparableRvItem() { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt index e72437fe6..5006ef115 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt @@ -1,16 +1,16 @@ package com.topjohnwu.magisk.model.entity.recycler import android.graphics.drawable.Drawable -import com.skoumal.teanity.databinding.ComparableRvItem -import com.skoumal.teanity.extensions.addOnPropertyChangedCallback -import com.skoumal.teanity.rxbus.RxBus -import com.skoumal.teanity.util.KObservableField +import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.databinding.ComparableRvItem +import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback import com.topjohnwu.magisk.extensions.inject import com.topjohnwu.magisk.extensions.toggle import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.events.PolicyEnableEvent import com.topjohnwu.magisk.model.events.PolicyUpdateEvent +import com.topjohnwu.magisk.utils.KObservableField class PolicyRvItem(val item: MagiskPolicy, val icon: Drawable) : ComparableRvItem() { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SectionRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SectionRvItem.kt index 11fd9ebe3..9aad6d59e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SectionRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SectionRvItem.kt @@ -1,7 +1,7 @@ package com.topjohnwu.magisk.model.entity.recycler -import com.skoumal.teanity.databinding.ComparableRvItem import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.databinding.ComparableRvItem class SectionRvItem(val text: String) : ComparableRvItem() { override val layoutRes: Int = R.layout.item_section diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SpinnerRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SpinnerRvItem.kt index 92dcbf08f..7a69f924e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SpinnerRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SpinnerRvItem.kt @@ -1,7 +1,7 @@ package com.topjohnwu.magisk.model.entity.recycler -import com.skoumal.teanity.databinding.ComparableRvItem import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.databinding.ComparableRvItem class SpinnerRvItem(val item: String) : ComparableRvItem() { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/EventHandler.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/EventHandler.kt new file mode 100644 index 000000000..3823881cb --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/EventHandler.kt @@ -0,0 +1,18 @@ +package com.topjohnwu.magisk.model.events + +internal interface EventHandler { + + /** + * Called for all [ViewEvent]s published by associated viewModel. + * For [SimpleViewEvent]s, both this and [onSimpleEventDispatched] + * methods are called - you can choose the way how you handle them. + */ + fun onEventDispatched(event: ViewEvent) {} + + /** + * Called for all [SimpleViewEvent]s published by associated viewModel. + * Both this and [onEventDispatched] methods are called - you can choose + * the way how you handle them. + */ + fun onSimpleEventDispatched(event: Int) {} +} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt index 5f6335bfe..b03814b54 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt @@ -1,6 +1,6 @@ package com.topjohnwu.magisk.model.events -import com.skoumal.teanity.rxbus.RxBus +import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.entity.recycler.HideProcessRvItem import com.topjohnwu.magisk.model.entity.recycler.ModuleRvItem diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/SimpleViewEvent.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/SimpleViewEvent.kt new file mode 100644 index 000000000..8b0b22bdd --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/SimpleViewEvent.kt @@ -0,0 +1,7 @@ +package com.topjohnwu.magisk.model.events + +import com.topjohnwu.magisk.model.events.ViewEvent + +class SimpleViewEvent( + val event: Int +) : ViewEvent() \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt new file mode 100644 index 000000000..59ee28af2 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt @@ -0,0 +1,28 @@ +package com.topjohnwu.magisk.model.events + +import android.content.Context +import androidx.annotation.StringRes +import com.google.android.material.snackbar.Snackbar +import com.topjohnwu.magisk.model.events.ViewEvent + +class SnackbarEvent private constructor( + @StringRes private val messageRes: Int, + private val messageString: String?, + val length: Int, + val f: Snackbar.() -> Unit +) : ViewEvent() { + + constructor( + @StringRes messageRes: Int, + length: Int = Snackbar.LENGTH_SHORT, + f: Snackbar.() -> Unit = {} + ) : this(messageRes, null, length, f) + + constructor( + message: String, + length: Int = Snackbar.LENGTH_SHORT, + f: Snackbar.() -> Unit = {} + ) : this(-1, message, length, f) + + fun message(context: Context): String = messageString ?: context.getString(messageRes) +} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEventObserver.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEventObserver.kt new file mode 100644 index 000000000..7f98f966b --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEventObserver.kt @@ -0,0 +1,17 @@ +package com.topjohnwu.magisk.model.events + +import androidx.lifecycle.Observer + +/** + * Observer for [ViewEvent]s, which automatically checks if event was handled + */ +class ViewEventObserver(private val onEventUnhandled: (ViewEvent) -> Unit) : Observer { + override fun onChanged(event: ViewEvent?) { + event?.let { + if (!it.handled) { + it.handled = true + onEventUnhandled(it) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt index 206e765d1..f8e273bd7 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt @@ -1,10 +1,19 @@ package com.topjohnwu.magisk.model.events import android.app.Activity -import com.skoumal.teanity.viewevents.ViewEvent import com.topjohnwu.magisk.model.entity.module.Repo import io.reactivex.subjects.PublishSubject +/** + * Class for passing events from ViewModels to Activities/Fragments + * Variable [handled] used so each event is handled only once + * (see https://medium.com/google-developers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150) + * Use [ViewEventObserver] for observing these events + */ +abstract class ViewEvent { + + var handled = false +} data class OpenLinkEvent(val url: String) : ViewEvent() @@ -35,4 +44,4 @@ class PermissionEvent( class BackPressEvent : ViewEvent() -class DieEvent : ViewEvent() \ No newline at end of file +class DieEvent : ViewEvent() diff --git a/app/src/main/java/com/topjohnwu/magisk/model/navigation/MagiskNavigationEvent.kt b/app/src/main/java/com/topjohnwu/magisk/model/navigation/MagiskNavigationEvent.kt index 793c2a4c2..fb11e1ad0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/navigation/MagiskNavigationEvent.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/navigation/MagiskNavigationEvent.kt @@ -4,10 +4,12 @@ import android.os.Bundle import androidx.annotation.AnimRes import androidx.annotation.AnimatorRes import androidx.fragment.app.Fragment -import com.skoumal.teanity.viewevents.NavigationDslMarker -import com.skoumal.teanity.viewevents.ViewEvent +import com.topjohnwu.magisk.model.events.ViewEvent import kotlin.reflect.KClass +@DslMarker +annotation class NavigationDslMarker + class MagiskNavigationEvent( val navDirections: MagiskNavDirectionsBuilder, val navOptions: MagiskNavOptions, diff --git a/app/src/main/java/com/topjohnwu/magisk/tasks/FlashZip.kt b/app/src/main/java/com/topjohnwu/magisk/tasks/FlashZip.kt index 3ae10b1a3..2e9e9ac36 100644 --- a/app/src/main/java/com/topjohnwu/magisk/tasks/FlashZip.kt +++ b/app/src/main/java/com/topjohnwu/magisk/tasks/FlashZip.kt @@ -2,11 +2,11 @@ package com.topjohnwu.magisk.tasks import android.content.Context import android.net.Uri -import com.skoumal.teanity.extensions.subscribeK import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.extensions.fileName import com.topjohnwu.magisk.extensions.inject import com.topjohnwu.magisk.extensions.readUri +import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.utils.unzip import com.topjohnwu.superuser.Shell import io.reactivex.Single diff --git a/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt b/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt index 9ef1c8d72..9547adfb2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt +++ b/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt @@ -6,7 +6,6 @@ import android.os.Build import android.text.TextUtils import androidx.annotation.MainThread import androidx.annotation.WorkerThread -import com.skoumal.teanity.extensions.subscribeK import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.data.network.GithubRawServices diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt index 86d13c915..a4c623faa 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt @@ -4,13 +4,13 @@ import android.content.Intent import android.os.Bundle import androidx.core.view.GravityCompat import androidx.fragment.app.Fragment -import com.skoumal.teanity.extensions.addOnPropertyChangedCallback import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const.Key.OPEN_SECTION import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R import com.topjohnwu.magisk.databinding.ActivityMainBinding +import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback import com.topjohnwu.magisk.model.navigation.Navigation import com.topjohnwu.magisk.ui.base.MagiskActivity import com.topjohnwu.magisk.ui.hide.MagiskHideFragment @@ -43,10 +43,6 @@ open class MainActivity : MagiskActivity() { SettingsFragment::class ) - /*override fun getDarkTheme(): Int { - return R.style.AppTheme_Dark - }*/ - override fun onCreate(savedInstanceState: Bundle?) { if (!SplashActivity.DONE) { startActivity(Intent(this, ClassMap[SplashActivity::class.java])) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt index f47d314db..797c99a0f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt @@ -6,9 +6,11 @@ import android.content.Intent import android.content.res.Configuration import android.os.Bundle import androidx.annotation.CallSuper +import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.collection.SparseArrayCompat import androidx.core.net.toUri +import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentTransaction @@ -19,13 +21,12 @@ import com.karumi.dexter.listener.PermissionRequest import com.karumi.dexter.listener.multi.MultiplePermissionsListener import com.ncapdevi.fragnav.FragNavController import com.ncapdevi.fragnav.FragNavTransactionOptions -import com.skoumal.teanity.view.TeanityActivity -import com.skoumal.teanity.viewevents.ViewEvent +import com.topjohnwu.magisk.model.events.EventHandler +import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.extensions.set -import com.topjohnwu.magisk.model.events.BackPressEvent -import com.topjohnwu.magisk.model.events.PermissionEvent -import com.topjohnwu.magisk.model.events.ViewActionEvent +import com.topjohnwu.magisk.extensions.snackbar +import com.topjohnwu.magisk.model.events.* import com.topjohnwu.magisk.model.navigation.MagiskAnimBuilder import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent import com.topjohnwu.magisk.model.navigation.Navigator @@ -39,17 +40,26 @@ import kotlin.reflect.KClass typealias RequestCallback = MagiskActivity<*, *>.(Int, Intent?) -> Unit abstract class MagiskActivity : - TeanityActivity(), FragNavController.RootFragmentListener, + AppCompatActivity(), FragNavController.RootFragmentListener, EventHandler, Navigator, FragNavController.TransactionListener { - override val numberOfRootFragments: Int get() = baseFragments.size - override val baseFragments: List> = listOf() - private val resultCallbacks = SparseArrayCompat() + protected lateinit var binding: Binding + protected abstract val layoutRes: Int + protected abstract val viewModel: ViewModel + protected open val snackbarView get() = binding.root + protected open val navHostId: Int = 0 + private val viewEventObserver = ViewEventObserver { + onEventDispatched(it) + if (it is SimpleViewEvent) { + onSimpleEventDispatched(it.event) + } + } + private val resultCallbacks = SparseArrayCompat() protected open val defaultPosition: Int = 0 - protected val navigationController get() = if (navHostId == 0) null else _navigationController + private val navigationController get() = if (navHostId == 0) null else _navigationController private val _navigationController by lazy { if (navHostId == 0) throw IllegalStateException("Did you forget to override \"navHostId\"?") FragNavController(supportFragmentManager, navHostId) @@ -58,6 +68,9 @@ abstract class MagiskActivity> = listOf() + init { val theme = if (Config.darkTheme) { AppCompatDelegate.MODE_NIGHT_YES @@ -79,6 +92,14 @@ abstract class MagiskActivity(this, layoutRes).apply { + setVariable(BR.viewModel, viewModel) + lifecycleOwner = this@MagiskActivity + } + + viewModel.viewEvents.observe(this, viewEventObserver) + navigationController?.apply { rootFragmentListener = this@MagiskActivity transactionListener = this@MagiskActivity @@ -95,6 +116,7 @@ abstract class MagiskActivity snackbar(snackbarView, event.message(this), event.length, event.f) is BackPressEvent -> onBackPressed() is MagiskNavigationEvent -> navigateTo(event) is ViewActionEvent -> event.action(this) @@ -230,11 +252,7 @@ abstract class MagiskActivity : - TeanityFragment(), Navigator { + Fragment(), Navigator, EventHandler { protected val activity get() = requireActivity() as MagiskActivity<*, *> + protected lateinit var binding: Binding + protected abstract val layoutRes: Int + protected abstract val viewModel: ViewModel + protected open val snackbarView get() = binding.root + protected val navController get() = binding.root.findNavController() + private val viewEventObserver = ViewEventObserver { + onEventDispatched(it) + if (it is SimpleViewEvent) { + onSimpleEventDispatched(it.event) + } + } // We don't need nested fragments override val baseFragments: List> = listOf() override fun navigateTo(event: MagiskNavigationEvent) = activity.navigateTo(event) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + viewModel.viewEvents.observe(this, viewEventObserver) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DataBindingUtil.inflate(inflater, layoutRes, container, false).apply { + setVariable(BR.viewModel, viewModel) + lifecycleOwner = this@MagiskFragment + } + + return binding.root + } + @CallSuper override fun onEventDispatched(event: ViewEvent) { super.onEventDispatched(event) when (event) { + is SnackbarEvent -> snackbar(snackbarView, event.message(requireContext()), event.length, event.f) is BackPressEvent -> activity.onBackPressed() is MagiskNavigationEvent -> navigateTo(event) is ViewActionEvent -> event.action(requireActivity()) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt index c1906cbd9..2cee6a32f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt @@ -2,14 +2,14 @@ package com.topjohnwu.magisk.ui.base import android.app.Activity import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.skoumal.teanity.extensions.doOnSubscribeUi -import com.skoumal.teanity.extensions.subscribeK -import com.skoumal.teanity.util.KObservableField -import com.skoumal.teanity.viewmodel.LoadingViewModel +import com.topjohnwu.magisk.extensions.doOnSubscribeUi import com.topjohnwu.magisk.extensions.get +import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.model.events.BackPressEvent import com.topjohnwu.magisk.model.events.PermissionEvent import com.topjohnwu.magisk.model.events.ViewActionEvent +import com.topjohnwu.magisk.utils.KObservableField +import com.topjohnwu.magisk.viewmodel.LoadingViewModel import io.reactivex.Observable import io.reactivex.subjects.PublishSubject diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt index 92fe4210c..c51c2883d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt @@ -7,21 +7,20 @@ import android.net.Uri import android.os.Handler import androidx.core.os.postDelayed import androidx.databinding.ObservableArrayList -import com.skoumal.teanity.databinding.ComparableRvItem -import com.skoumal.teanity.extensions.subscribeK -import com.skoumal.teanity.util.DiffObservableList -import com.skoumal.teanity.util.KObservableField -import com.skoumal.teanity.viewevents.SnackbarEvent import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.* import com.topjohnwu.magisk.model.entity.recycler.ConsoleRvItem +import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.model.flash.FlashResultListener import com.topjohnwu.magisk.model.flash.Flashing import com.topjohnwu.magisk.model.flash.Patching import com.topjohnwu.magisk.ui.base.MagiskViewModel +import com.topjohnwu.magisk.utils.DiffObservableList +import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.superuser.Shell import me.tatarka.bindingcollectionadapter2.ItemBinding import java.io.File diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt index 5e5a08992..762db9667 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt @@ -1,14 +1,12 @@ package com.topjohnwu.magisk.ui.hide import android.content.pm.ApplicationInfo -import com.skoumal.teanity.databinding.ComparableRvItem -import com.skoumal.teanity.extensions.addOnPropertyChangedCallback -import com.skoumal.teanity.extensions.subscribeK -import com.skoumal.teanity.rxbus.RxBus -import com.skoumal.teanity.util.DiffObservableList -import com.skoumal.teanity.util.KObservableField +import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.data.repository.MagiskRepository +import com.topjohnwu.magisk.databinding.ComparableRvItem +import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback +import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.toSingle import com.topjohnwu.magisk.extensions.update import com.topjohnwu.magisk.model.entity.recycler.HideProcessRvItem @@ -16,6 +14,8 @@ import com.topjohnwu.magisk.model.entity.recycler.HideRvItem import com.topjohnwu.magisk.model.entity.state.IndeterminateState import com.topjohnwu.magisk.model.events.HideProcessEvent import com.topjohnwu.magisk.ui.base.MagiskViewModel +import com.topjohnwu.magisk.utils.DiffObservableList +import com.topjohnwu.magisk.utils.KObservableField import me.tatarka.bindingcollectionadapter2.OnItemBind import timber.log.Timber diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt index 3d5ca84fa..2794205b6 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt @@ -1,8 +1,7 @@ package com.topjohnwu.magisk.ui.home import android.content.Context -import com.skoumal.teanity.extensions.subscribeK -import com.skoumal.teanity.viewevents.ViewEvent +import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Info @@ -10,6 +9,7 @@ import com.topjohnwu.magisk.R import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.databinding.FragmentMagiskBinding import com.topjohnwu.magisk.extensions.inject +import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.writeTo import com.topjohnwu.magisk.model.events.* import com.topjohnwu.magisk.ui.base.MagiskActivity diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt index 2c98a4190..6b6c902f2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt @@ -1,19 +1,13 @@ package com.topjohnwu.magisk.ui.home import android.content.pm.PackageManager -import com.skoumal.teanity.extensions.addOnPropertyChangedCallback -import com.skoumal.teanity.extensions.doOnSubscribeUi -import com.skoumal.teanity.extensions.subscribeK -import com.skoumal.teanity.util.KObservableField import com.topjohnwu.magisk.* import com.topjohnwu.magisk.data.repository.MagiskRepository -import com.topjohnwu.magisk.extensions.get -import com.topjohnwu.magisk.extensions.packageName -import com.topjohnwu.magisk.extensions.res -import com.topjohnwu.magisk.extensions.toggle +import com.topjohnwu.magisk.extensions.* import com.topjohnwu.magisk.model.events.* import com.topjohnwu.magisk.model.observer.Observer import com.topjohnwu.magisk.ui.base.MagiskViewModel +import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.SafetyNetHelper import com.topjohnwu.superuser.Shell diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt index 80c98c253..e0fb8aef7 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt @@ -6,7 +6,7 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View -import com.skoumal.teanity.viewevents.ViewEvent +import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.R import com.topjohnwu.magisk.databinding.FragmentLogBinding import com.topjohnwu.magisk.model.events.PageChangedEvent diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt index 279ac5036..6493d5fde 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt @@ -1,18 +1,16 @@ package com.topjohnwu.magisk.ui.log import android.content.res.Resources -import com.skoumal.teanity.databinding.ComparableRvItem -import com.skoumal.teanity.extensions.addOnPropertyChangedCallback -import com.skoumal.teanity.extensions.doOnSubscribeUi -import com.skoumal.teanity.extensions.subscribeK -import com.skoumal.teanity.util.DiffObservableList -import com.skoumal.teanity.util.KObservableField -import com.skoumal.teanity.viewevents.SnackbarEvent +import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R import com.topjohnwu.magisk.data.repository.LogRepository +import com.topjohnwu.magisk.databinding.ComparableRvItem +import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback +import com.topjohnwu.magisk.extensions.doOnSubscribeUi +import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.model.binding.BindingAdapter import com.topjohnwu.magisk.model.entity.recycler.ConsoleRvItem import com.topjohnwu.magisk.model.entity.recycler.LogItemRvItem @@ -20,6 +18,8 @@ import com.topjohnwu.magisk.model.entity.recycler.LogRvItem import com.topjohnwu.magisk.model.entity.recycler.MagiskLogRvItem import com.topjohnwu.magisk.model.events.PageChangedEvent import com.topjohnwu.magisk.ui.base.MagiskViewModel +import com.topjohnwu.magisk.utils.DiffObservableList +import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.superuser.Shell import me.tatarka.bindingcollectionadapter2.BindingViewPagerAdapter import me.tatarka.bindingcollectionadapter2.OnItemBind diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt index f83af2c5a..35d74aefd 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt @@ -1,17 +1,11 @@ package com.topjohnwu.magisk.ui.module import android.content.res.Resources -import com.skoumal.teanity.databinding.ComparableRvItem -import com.skoumal.teanity.extensions.addOnPropertyChangedCallback -import com.skoumal.teanity.extensions.doOnSuccessUi -import com.skoumal.teanity.extensions.subscribeK -import com.skoumal.teanity.util.DiffObservableList -import com.skoumal.teanity.util.KObservableField import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.R import com.topjohnwu.magisk.data.database.RepoDao -import com.topjohnwu.magisk.extensions.toSingle -import com.topjohnwu.magisk.extensions.update +import com.topjohnwu.magisk.databinding.ComparableRvItem +import com.topjohnwu.magisk.extensions.* import com.topjohnwu.magisk.model.entity.module.Module import com.topjohnwu.magisk.model.entity.recycler.ModuleRvItem import com.topjohnwu.magisk.model.entity.recycler.RepoRvItem @@ -21,6 +15,8 @@ import com.topjohnwu.magisk.model.events.OpenChangelogEvent import com.topjohnwu.magisk.model.events.OpenFilePickerEvent import com.topjohnwu.magisk.tasks.RepoUpdater import com.topjohnwu.magisk.ui.base.MagiskViewModel +import com.topjohnwu.magisk.utils.DiffObservableList +import com.topjohnwu.magisk.utils.KObservableField import io.reactivex.Single import io.reactivex.disposables.Disposable import me.tatarka.bindingcollectionadapter2.OnItemBind diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt index 17a1e16fa..7c9799abb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt @@ -8,7 +8,7 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import androidx.recyclerview.widget.RecyclerView -import com.skoumal.teanity.viewevents.ViewEvent +import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt index 6fa93d20a..c10d0f567 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt @@ -6,7 +6,7 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.widget.SearchView -import com.skoumal.teanity.viewevents.ViewEvent +import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R import com.topjohnwu.magisk.databinding.FragmentReposBinding diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt index 0f5f82aba..ac11098f0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt @@ -13,14 +13,13 @@ import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.SwitchPreferenceCompat -import com.skoumal.teanity.extensions.subscribeK -import com.skoumal.teanity.util.KObservableField import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R import com.topjohnwu.magisk.data.database.RepoDao import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding +import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.toLangTag import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.entity.internal.Configuration diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt index 954696716..be17f49bb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt @@ -2,21 +2,21 @@ package com.topjohnwu.magisk.ui.superuser import android.content.pm.PackageManager import android.content.res.Resources -import com.skoumal.teanity.databinding.ComparableRvItem -import com.skoumal.teanity.extensions.applySchedulers -import com.skoumal.teanity.extensions.subscribeK -import com.skoumal.teanity.rxbus.RxBus -import com.skoumal.teanity.util.DiffObservableList -import com.skoumal.teanity.viewevents.SnackbarEvent +import com.topjohnwu.magisk.utils.RxBus +import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.R import com.topjohnwu.magisk.data.database.PolicyDao +import com.topjohnwu.magisk.databinding.ComparableRvItem +import com.topjohnwu.magisk.extensions.applySchedulers +import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.toggle import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem import com.topjohnwu.magisk.model.events.PolicyEnableEvent import com.topjohnwu.magisk.model.events.PolicyUpdateEvent import com.topjohnwu.magisk.ui.base.MagiskViewModel +import com.topjohnwu.magisk.utils.DiffObservableList import com.topjohnwu.magisk.utils.FingerprintHelper import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt index dc4f54b09..3ad04fa96 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt @@ -5,7 +5,7 @@ import android.os.Build import android.os.Bundle import android.text.TextUtils import android.view.Window -import com.skoumal.teanity.viewevents.ViewEvent +import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.R import com.topjohnwu.magisk.databinding.ActivityRequestBinding import com.topjohnwu.magisk.model.entity.MagiskPolicy diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt index 6cb43d425..e47412747 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt @@ -9,21 +9,21 @@ import android.graphics.drawable.Drawable import android.hardware.fingerprint.FingerprintManager import android.os.CountDownTimer import android.text.TextUtils -import com.skoumal.teanity.databinding.ComparableRvItem -import com.skoumal.teanity.extensions.addOnPropertyChangedCallback -import com.skoumal.teanity.util.DiffObservableList -import com.skoumal.teanity.util.KObservableField import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R import com.topjohnwu.magisk.data.database.PolicyDao +import com.topjohnwu.magisk.databinding.ComparableRvItem +import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback import com.topjohnwu.magisk.extensions.now import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.entity.recycler.SpinnerRvItem import com.topjohnwu.magisk.model.entity.toPolicy import com.topjohnwu.magisk.model.events.DieEvent import com.topjohnwu.magisk.ui.base.MagiskViewModel +import com.topjohnwu.magisk.utils.DiffObservableList import com.topjohnwu.magisk.utils.FingerprintHelper +import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.SuConnector import me.tatarka.bindingcollectionadapter2.BindingListViewAdapter import me.tatarka.bindingcollectionadapter2.ItemBinding diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt b/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt index ad61131f8..17b0a2552 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt @@ -16,9 +16,9 @@ import androidx.recyclerview.widget.RecyclerView import androidx.viewpager.widget.ViewPager import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.navigation.NavigationView -import com.skoumal.teanity.extensions.subscribeK import com.topjohnwu.magisk.R import com.topjohnwu.magisk.extensions.replaceRandomWithSpecial +import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.model.entity.state.IndeterminateState import io.reactivex.Observable import io.reactivex.disposables.Disposable diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/DiffObservableList.kt b/app/src/main/java/com/topjohnwu/magisk/utils/DiffObservableList.kt new file mode 100644 index 000000000..c7c61d968 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/DiffObservableList.kt @@ -0,0 +1,234 @@ +package com.topjohnwu.magisk.utils + +import androidx.annotation.MainThread +import androidx.databinding.ListChangeRegistry +import androidx.databinding.ObservableList +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListUpdateCallback +import java.util.* +import kotlin.collections.ArrayList + +/** + * @param callback The callback that controls the behavior of the DiffObservableList. + * @param detectMoves True if DiffUtil should try to detect moved items, false otherwise. + */ +open class DiffObservableList( + private val callback: Callback, + private val detectMoves: Boolean = true +) : AbstractList(), ObservableList { + + private val LIST_LOCK = Object() + private var list: MutableList = ArrayList() + private val listeners = ListChangeRegistry() + private val listCallback = ObservableListUpdateCallback() + + override val size: Int get() = list.size + + /** + * Calculates the list of update operations that can convert this list into the given one. + * + * @param newItems The items that this list will be set to. + * @return A DiffResult that contains the information about the edit sequence to covert this + * list into the given one. + */ + fun calculateDiff(newItems: List): DiffUtil.DiffResult { + val frozenList = synchronized(LIST_LOCK) { + ArrayList(list) + } + return doCalculateDiff(frozenList, newItems) + } + + private fun doCalculateDiff(oldItems: List, newItems: List?): DiffUtil.DiffResult { + return DiffUtil.calculateDiff(object : DiffUtil.Callback() { + override fun getOldListSize() = oldItems.size + + override fun getNewListSize() = newItems?.size ?: 0 + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val oldItem = oldItems[oldItemPosition] + val newItem = newItems!![newItemPosition] + return callback.areItemsTheSame(oldItem, newItem) + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val oldItem = oldItems[oldItemPosition] + val newItem = newItems!![newItemPosition] + return callback.areContentsTheSame(oldItem, newItem) + } + }, detectMoves) + } + + /** + * Updates the contents of this list to the given one using the DiffResults to dispatch change + * notifications. + * + * @param newItems The items to set this list to. + * @param diffResult The diff results to dispatch change notifications. + */ + @MainThread + fun update(newItems: List, diffResult: DiffUtil.DiffResult) { + synchronized(LIST_LOCK) { + list = newItems.toMutableList() + } + diffResult.dispatchUpdatesTo(listCallback) + } + + /** + * Sets this list to the given items. This is a convenience method for calling [ ][.calculateDiff] followed by [.update]. + * + * + * **Warning!** If the lists are large this operation may be too slow for the main thread. In + * that case, you should call [.calculateDiff] on a background thread and then + * [.update] on the main thread. + * + * @param newItems The items to set this list to. + */ + @MainThread + fun update(newItems: List) { + val diffResult = doCalculateDiff(list, newItems) + list = newItems.toMutableList() + diffResult.dispatchUpdatesTo(listCallback) + } + + override fun addOnListChangedCallback(listener: ObservableList.OnListChangedCallback>) { + listeners.add(listener) + } + + override fun removeOnListChangedCallback(listener: ObservableList.OnListChangedCallback>) { + listeners.remove(listener) + } + + override fun get(index: Int): T { + return list[index] + } + + override fun add(element: T): Boolean { + list.add(element) + notifyAdd(size - 1, 1) + return true + } + + override fun add(index: Int, element: T) { + list.add(index, element) + notifyAdd(index, 1) + } + + override fun addAll(elements: Collection): Boolean { + val oldSize = size + val added = list.addAll(elements) + if (added) { + notifyAdd(oldSize, size - oldSize) + } + return added + } + + override fun addAll(index: Int, elements: Collection): Boolean { + val added = list.addAll(index, elements) + if (added) { + notifyAdd(index, elements.size) + } + return added + } + + override fun clear() { + val oldSize = size + list.clear() + if (oldSize != 0) { + notifyRemove(0, oldSize) + } + } + + override fun remove(element: T): Boolean { + val index = indexOf(element) + return if (index >= 0) { + removeAt(index) + true + } else { + false + } + } + + override fun removeAt(index: Int): T { + val element = list.removeAt(index) + notifyRemove(index, 1) + return element + } + + fun removeLast(): T? { + if (size > 0) { + val index = size - 1 + return removeAt(index) + } + return null + } + + override fun set(index: Int, element: T): T { + val old = list.set(index, element) + listeners.notifyChanged(this, index, 1) + return old + } + + private fun notifyAdd(start: Int, count: Int) { + listeners.notifyInserted(this, start, count) + } + + private fun notifyRemove(start: Int, count: Int) { + listeners.notifyRemoved(this, start, count) + } + + /** + * A Callback class used by DiffUtil while calculating the diff between two lists. + */ + interface Callback { + /** + * Called by the DiffUtil to decide whether two object represent the same Item. + * + * + * For example, if your items have unique ids, this method should check their id equality. + * + * @param oldItem The old item. + * @param newItem The new item. + * @return True if the two items represent the same object or false if they are different. + */ + fun areItemsTheSame(oldItem: T, newItem: T): Boolean + + /** + * Called by the DiffUtil when it wants to check whether two items have the same data. + * DiffUtil uses this information to detect if the contents of an item has changed. + * + * + * DiffUtil uses this method to check equality instead of [Object.equals] so + * that you can change its behavior depending on your UI. + * + * + * This method is called only if [.areItemsTheSame] returns `true` for + * these items. + * + * @param oldItem The old item. + * @param newItem The new item which replaces the old item. + * @return True if the contents of the items are the same or false if they are different. + */ + fun areContentsTheSame(oldItem: T, newItem: T): Boolean + } + + inner class ObservableListUpdateCallback : ListUpdateCallback { + override fun onChanged(position: Int, count: Int, payload: Any?) { + listeners.notifyChanged(this@DiffObservableList, position, count) + } + + override fun onMoved(fromPosition: Int, toPosition: Int) { + listeners.notifyMoved(this@DiffObservableList, fromPosition, toPosition, 1) + } + + override fun onInserted(position: Int, count: Int) { + modCount += 1 + listeners.notifyInserted(this@DiffObservableList, position, count) + } + + override fun onRemoved(position: Int, count: Int) { + modCount += 1 + listeners.notifyRemoved(this@DiffObservableList, position, count) + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/KItemDecoration.kt b/app/src/main/java/com/topjohnwu/magisk/utils/KItemDecoration.kt new file mode 100644 index 000000000..0fc5c4f5c --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/KItemDecoration.kt @@ -0,0 +1,117 @@ +package com.topjohnwu.magisk.utils + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Rect +import android.graphics.drawable.Drawable +import android.view.View +import androidx.annotation.DrawableRes +import androidx.core.view.get +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.RecyclerView +import com.topjohnwu.magisk.extensions.drawableCompat + +class KItemDecoration( + private val context: Context, + @RecyclerView.Orientation private val orientation: Int +) : + RecyclerView.ItemDecoration() { + + private val bounds = Rect() + private var divider: Drawable? = null + var showAfterLast = true + + fun setDeco(@DrawableRes drawable: Int) = apply { + setDeco(context.drawableCompat(drawable)) + } + + fun setDeco(drawable: Drawable?) = apply { + divider = drawable + } + + override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) { + parent.layoutManager ?: return + + divider?.let { + if (orientation == DividerItemDecoration.VERTICAL) { + drawVertical(canvas, parent, it) + } else { + drawHorizontal(canvas, parent, it) + } + } + } + + private fun drawVertical(canvas: Canvas, parent: RecyclerView, drawable: Drawable) { + canvas.save() + val left: Int + val right: Int + if (parent.clipToPadding) { + left = parent.paddingLeft + right = parent.width - parent.paddingRight + canvas.clipRect( + left, parent.paddingTop, right, + parent.height - parent.paddingBottom + ) + } else { + left = 0 + right = parent.width + } + + val to = if (showAfterLast) parent.childCount else parent.childCount - 1 + + (0 until to) + .map { parent[it] } + .forEach { child -> + parent.getDecoratedBoundsWithMargins(child, bounds) + val bottom = bounds.bottom + Math.round(child.translationY) + val top = bottom - drawable.intrinsicHeight + drawable.setBounds(left, top, right, bottom) + drawable.draw(canvas) + } + canvas.restore() + } + + private fun drawHorizontal(canvas: Canvas, parent: RecyclerView, drawable: Drawable) { + canvas.save() + val top: Int + val bottom: Int + if (parent.clipToPadding) { + top = parent.paddingTop + bottom = parent.height - parent.paddingBottom + canvas.clipRect( + parent.paddingLeft, top, + parent.width - parent.paddingRight, bottom + ) + } else { + top = 0 + bottom = parent.height + } + + val to = if (showAfterLast) parent.childCount else parent.childCount - 1 + + (0 until to) + .map { parent[it] } + .forEach { child -> + parent.layoutManager!!.getDecoratedBoundsWithMargins(child, bounds) + val right = bounds.right + Math.round(child.translationX) + val left = right - drawable.intrinsicWidth + drawable.setBounds(left, top, right, bottom) + drawable.draw(canvas) + } + canvas.restore() + } + + override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { + if (parent.getChildAdapterPosition(view) == state.itemCount - 1) { + outRect.setEmpty() + return + } + + if (orientation == RecyclerView.VERTICAL) { + outRect.set(0, 0, 0, divider?.intrinsicHeight ?: 0) + } else { + outRect.set(0, 0, divider?.intrinsicWidth ?: 0, 0) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/KObservableField.kt b/app/src/main/java/com/topjohnwu/magisk/utils/KObservableField.kt new file mode 100644 index 000000000..7ecf125f4 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/KObservableField.kt @@ -0,0 +1,49 @@ +package com.topjohnwu.magisk.utils + +import androidx.databinding.Observable +import androidx.databinding.ObservableField +import java.io.Serializable + +/** + * Kotlin version of [ObservableField]. + * You can define if wrapped type is Nullable or not. + * You can use kotlin get/set syntax for value + */ +class KObservableField : ObservableField, Serializable { + + var value: T + set(value) { + if (field != value) { + field = value + notifyChange() + } + } + + constructor(init: T) { + value = init + } + + constructor(init: T, vararg dependencies: Observable) : super(*dependencies) { + value = init + } + + @Deprecated( + message = "Needed for data binding, use KObservableField.value syntax from code", + replaceWith = ReplaceWith("value") + ) + override fun get(): T { + return value + } + + @Deprecated( + message = "Needed for data binding, use KObservableField.value = ... syntax from code", + replaceWith = ReplaceWith("value = newValue") + ) + override fun set(newValue: T) { + value = newValue + } + + override fun toString(): String { + return "KObservableField(value=$value)" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt index 74996f23a..5f45dc8a7 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt @@ -3,8 +3,8 @@ package com.topjohnwu.magisk.utils import android.content.ComponentName import android.content.Context import android.widget.Toast -import com.skoumal.teanity.extensions.subscribeK import com.topjohnwu.magisk.* +import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.signing.JarMap diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/RxBus.kt b/app/src/main/java/com/topjohnwu/magisk/utils/RxBus.kt new file mode 100644 index 000000000..c27d898b8 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/RxBus.kt @@ -0,0 +1,36 @@ +package com.topjohnwu.magisk.utils + +import io.reactivex.Observable +import io.reactivex.subjects.PublishSubject + +class RxBus { + + private val _bus = PublishSubject.create() + + val bus: Observable get() = _bus + + fun post(event: Event) { + _bus.onNext(event) + } + + fun post(event: Int) { + _bus.onNext(SimpleEvent(event)) + } + + inline fun register(noinline predicate: (T) -> Boolean = { true }): Observable { + return bus + .ofType(T::class.java) + .filter(predicate) + } + + fun register(eventId: Int): Observable { + return bus + .ofType(SimpleEvent::class.java) + .map { it.eventId } + .filter { it == eventId } + } + + interface Event + + private class SimpleEvent(val eventId: Int) : Event +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt b/app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt deleted file mode 100644 index a9ad818ab..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt +++ /dev/null @@ -1,155 +0,0 @@ -package com.topjohnwu.magisk.view - -import android.content.Context -import android.content.DialogInterface -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.graphics.drawable.Drawable -import android.os.Bundle -import android.view.LayoutInflater -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import androidx.appcompat.app.AlertDialog -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding -import com.skoumal.teanity.util.KObservableField -import com.topjohnwu.magisk.BR -import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.databinding.DialogMagiskBaseBinding - -class MagiskDialog @JvmOverloads constructor( - context: Context, theme: Int = 0 -) : AlertDialog(context, theme) { - - private val binding: DialogMagiskBaseBinding - private val data = Data() - - init { - val layoutInflater = LayoutInflater.from(context) - binding = DataBindingUtil.inflate(layoutInflater, R.layout.dialog_magisk_base, null, false) - binding.setVariable(BR.data, data) - super.setView(binding.root) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - } - - inner class Data { - val icon = KObservableField(0) - val iconRaw = KObservableField(null) - val title = KObservableField("") - val message = KObservableField("") - - val buttonPositive = Button() - val buttonNeutral = Button() - val buttonNegative = Button() - val buttonIDGAF = Button() - } - - enum class ButtonType { - POSITIVE, NEUTRAL, NEGATIVE, IDGAF - } - - inner class Button { - val icon = KObservableField(0) - val title = KObservableField("") - val isEnabled = KObservableField(true) - - var onClickAction: OnDialogButtonClickListener = {} - - fun clicked() { - onClickAction(this@MagiskDialog) - dismiss() - } - } - - inner class ButtonBuilder(private val button: Button) { - var icon: Int - get() = button.icon.value - set(value) { - button.icon.value = value - } - var title: CharSequence - get() = button.title.value - set(value) { - button.title.value = value - } - var titleRes: Int - get() = 0 - set(value) { - button.title.value = context.getString(value) - } - var isEnabled: Boolean - get() = button.isEnabled.value - set(value) { - button.isEnabled.value = value - } - - fun onClick(listener: OnDialogButtonClickListener) { - button.onClickAction = listener - } - } - - fun applyTitle(@StringRes stringRes: Int) = - apply { data.title.value = context.getString(stringRes) } - - fun applyTitle(title: CharSequence) = - apply { data.title.value = title } - - fun applyMessage(@StringRes stringRes: Int) = - apply { data.message.value = context.getString(stringRes) } - - fun applyMessage(message: CharSequence) = - apply { data.message.value = message } - - fun applyIcon(@DrawableRes drawableRes: Int) = - apply { data.icon.value = drawableRes } - - fun applyIcon(drawable: Drawable) = - apply { data.iconRaw.value = drawable } - - fun applyButton(buttonType: ButtonType, builder: ButtonBuilder.() -> Unit) = apply { - val button = when (buttonType) { - ButtonType.POSITIVE -> data.buttonPositive - ButtonType.NEUTRAL -> data.buttonNeutral - ButtonType.NEGATIVE -> data.buttonNegative - ButtonType.IDGAF -> data.buttonIDGAF - } - ButtonBuilder(button).apply(builder) - } - - fun cancellable(isCancellable: Boolean) = apply { - setCancelable(isCancellable) - } - - fun applyView(binding: Binding, body: Binding.() -> Unit) = - apply { - this.binding.dialogBaseContainer.removeAllViews() - this.binding.dialogBaseContainer.addView(binding.root) - binding.apply(body) - } - - fun onDismiss(callback: OnDialogButtonClickListener) = - apply { setOnDismissListener(callback) } - - fun onShow(callback: OnDialogButtonClickListener) = - apply { setOnShowListener(callback) } - - fun reveal() = apply { super.show() } - - //region Deprecated Members - @Deprecated("Use applyTitle instead", ReplaceWith("applyTitle")) - override fun setTitle(title: CharSequence?) = Unit - - @Deprecated("Use applyTitle instead", ReplaceWith("applyTitle")) - override fun setTitle(titleId: Int) = Unit - - @Deprecated("Use reveal()", ReplaceWith("reveal()")) - override fun show() { - } - //endregion -} - -typealias OnDialogButtonClickListener = (DialogInterface) -> Unit \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.kt b/app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.kt index 4a814104a..a7f16318e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.kt @@ -4,10 +4,10 @@ import android.content.Context import android.view.LayoutInflater import android.widget.TextView import androidx.appcompat.app.AlertDialog -import com.skoumal.teanity.extensions.subscribeK import com.topjohnwu.magisk.R import com.topjohnwu.magisk.data.repository.StringRepository import com.topjohnwu.magisk.extensions.inject +import com.topjohnwu.magisk.extensions.subscribeK import io.reactivex.Completable import io.reactivex.Single import ru.noties.markwon.Markwon diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.kt b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.kt index 35f790d23..8eb37107b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.kt @@ -8,9 +8,9 @@ import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R import com.topjohnwu.magisk.extensions.cachedFile import com.topjohnwu.magisk.extensions.reboot +import com.topjohnwu.magisk.net.Networking import com.topjohnwu.magisk.tasks.MagiskInstaller import com.topjohnwu.magisk.utils.Utils -import com.topjohnwu.magisk.net.Networking import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.internal.UiThreadHandler diff --git a/app/src/main/java/com/topjohnwu/magisk/viewmodel/LoadingViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/viewmodel/LoadingViewModel.kt new file mode 100644 index 000000000..4ff9deb62 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/viewmodel/LoadingViewModel.kt @@ -0,0 +1,78 @@ +package com.topjohnwu.magisk.viewmodel + +import androidx.databinding.Bindable +import com.topjohnwu.magisk.BR +import io.reactivex.* + +abstract class LoadingViewModel(defaultState: State = State.LOADING) : + StatefulViewModel(defaultState) { + + val loading @Bindable get() = state == State.LOADING + val loaded @Bindable get() = state == State.LOADED + val loadingFailed @Bindable get() = state == State.LOADING_FAILED + + @Deprecated( + "Direct access is recommended since 0.2. This access method will be removed in 1.0", + ReplaceWith("state = State.LOADING", "com.topjohnwu.magisk.viewmodel.LoadingViewModel.State"), + DeprecationLevel.WARNING + ) + fun setLoading() { + state = State.LOADING + } + + @Deprecated( + "Direct access is recommended since 0.2. This access method will be removed in 1.0", + ReplaceWith("state = State.LOADED", "com.topjohnwu.magisk.viewmodel.LoadingViewModel.State"), + DeprecationLevel.WARNING + ) + fun setLoaded() { + state = State.LOADED + } + + @Deprecated( + "Direct access is recommended since 0.2. This access method will be removed in 1.0", + ReplaceWith("state = State.LOADING_FAILED", "com.topjohnwu.magisk.viewmodel.LoadingViewModel.State"), + DeprecationLevel.WARNING + ) + fun setLoadingFailed() { + state = State.LOADING_FAILED + } + + override fun notifyStateChanged() { + notifyPropertyChanged(BR.loading) + notifyPropertyChanged(BR.loaded) + notifyPropertyChanged(BR.loadingFailed) + } + + enum class State { + LOADED, LOADING, LOADING_FAILED + } + + //region Rx + protected fun Observable.applyViewModel(viewModel: LoadingViewModel, allowFinishing: Boolean = true) = + doOnSubscribe { viewModel.state = State.LOADING } + .doOnError { viewModel.state = State.LOADING_FAILED } + .doOnNext { if (allowFinishing) viewModel.state = State.LOADED } + + protected fun Single.applyViewModel(viewModel: LoadingViewModel, allowFinishing: Boolean = true) = + doOnSubscribe { viewModel.state = State.LOADING } + .doOnError { viewModel.state = State.LOADING_FAILED } + .doOnSuccess { if (allowFinishing) viewModel.state = State.LOADED } + + protected fun Maybe.applyViewModel(viewModel: LoadingViewModel, allowFinishing: Boolean = true) = + doOnSubscribe { viewModel.state = State.LOADING } + .doOnError { viewModel.state = State.LOADING_FAILED } + .doOnComplete { if (allowFinishing) viewModel.state = State.LOADED } + .doOnSuccess { if (allowFinishing) viewModel.state = State.LOADED } + + protected fun Flowable.applyViewModel(viewModel: LoadingViewModel, allowFinishing: Boolean = true) = + doOnSubscribe { viewModel.state = State.LOADING } + .doOnError { viewModel.state = State.LOADING_FAILED } + .doOnNext { if (allowFinishing) viewModel.state = State.LOADED } + + protected fun Completable.applyViewModel(viewModel: LoadingViewModel, allowFinishing: Boolean = true) = + doOnSubscribe { viewModel.state = State.LOADING } + .doOnError { viewModel.state = State.LOADING_FAILED } + .doOnComplete { if (allowFinishing) viewModel.state = State.LOADED } + //endregion +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/viewmodel/ObservableViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/viewmodel/ObservableViewModel.kt new file mode 100644 index 000000000..1299836d8 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/viewmodel/ObservableViewModel.kt @@ -0,0 +1,46 @@ +package com.topjohnwu.magisk.viewmodel + +import androidx.databinding.Observable +import androidx.databinding.PropertyChangeRegistry +import androidx.lifecycle.ViewModel + +/** + * Copy of [android.databinding.BaseObservable] which extends [ViewModel] + */ +abstract class ObservableViewModel : TeanityViewModel(), Observable { + + @Transient + private var callbacks: PropertyChangeRegistry? = null + + @Synchronized + override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) { + if (callbacks == null) { + callbacks = PropertyChangeRegistry() + } + callbacks?.add(callback) + } + + @Synchronized + override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) { + callbacks?.remove(callback) + } + + /** + * Notifies listeners that all properties of this instance have changed. + */ + @Synchronized + fun notifyChange() { + callbacks?.notifyCallbacks(this, 0, null) + } + + /** + * Notifies listeners that a specific property has changed. The getter for the property + * that changes should be marked with [android.databinding.Bindable] to generate a field in + * `BR` to be used as `fieldId`. + * + * @param fieldId The generated BR id for the Bindable field. + */ + fun notifyPropertyChanged(fieldId: Int) { + callbacks?.notifyCallbacks(this, fieldId, null) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/viewmodel/StatefulViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/viewmodel/StatefulViewModel.kt new file mode 100644 index 000000000..5bf6229ff --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/viewmodel/StatefulViewModel.kt @@ -0,0 +1,15 @@ +package com.topjohnwu.magisk.viewmodel + +abstract class StatefulViewModel>( + val defaultState: State +) : ObservableViewModel() { + + var state: State = defaultState + set(value) { + field = value + notifyStateChanged() + } + + open fun notifyStateChanged() = Unit + +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/viewmodel/TeanityViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/viewmodel/TeanityViewModel.kt new file mode 100644 index 000000000..799a2305e --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/viewmodel/TeanityViewModel.kt @@ -0,0 +1,33 @@ +package com.topjohnwu.magisk.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.topjohnwu.magisk.model.events.SimpleViewEvent +import com.topjohnwu.magisk.model.events.ViewEvent +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable + +abstract class TeanityViewModel : ViewModel() { + + private val disposables = CompositeDisposable() + private val _viewEvents = MutableLiveData() + val viewEvents: LiveData get() = _viewEvents + + override fun onCleared() { + super.onCleared() + disposables.clear() + } + + fun Event.publish() { + _viewEvents.value = this + } + + fun Int.publish() { + _viewEvents.value = SimpleViewEvent(this) + } + + fun Disposable.add() { + disposables.add(this) + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_magisk_base.xml b/app/src/main/res/layout/dialog_magisk_base.xml deleted file mode 100644 index 134113f17..000000000 --- a/app/src/main/res/layout/dialog_magisk_base.xml +++ /dev/null @@ -1,276 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_magisk.xml b/app/src/main/res/layout/fragment_magisk.xml index e72cecda2..b762c87cf 100644 --- a/app/src/main/res/layout/fragment_magisk.xml +++ b/app/src/main/res/layout/fragment_magisk.xml @@ -5,7 +5,7 @@ - + diff --git a/build.gradle b/build.gradle index 7d5f95138..4d37e4df7 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ if (configPath.exists()) configPath.withInputStream { is -> props.load(is) } buildscript { - ext.kotlinVer = '1.3.50' + ext.vKotlin = '1.3.50' repositories { google() @@ -17,7 +17,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.5.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVer}" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${vKotlin}" // NOTE: Do not place your application dependencies here; they belong From 08b528dc4f5625d27baec76d458035bc696331f6 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 28 Sep 2019 03:37:24 -0400 Subject: [PATCH 10/50] Reorganize classes - Move base classes to its own package - Move most logic out of MagiskActivity to MainActivity --- app/proguard-rules.pro | 2 +- app/src/main/java/a/w.java | 2 +- .../{ui => }/base/BasePreferenceFragment.kt | 2 +- .../{model/worker => base}/DelegateWorker.kt | 2 +- .../topjohnwu/magisk/base/MagiskActivity.kt | 112 ++++++++ .../topjohnwu/magisk/base/MagiskFragment.kt | 51 ++++ .../{ => base}/viewmodel/LoadingViewModel.kt | 8 +- .../viewmodel}/MagiskViewModel.kt | 3 +- .../viewmodel/ObservableViewModel.kt | 2 +- .../{ => base}/viewmodel/StatefulViewModel.kt | 2 +- .../{ => base}/viewmodel/TeanityViewModel.kt | 2 +- .../model/entity/recycler/HideRvItem.kt | 2 +- .../model/entity/recycler/PolicyRvItem.kt | 2 +- .../magisk/model/events/EventHandler.kt | 7 + .../topjohnwu/magisk/model/events/RxEvents.kt | 2 +- .../magisk/model/events/SimpleViewEvent.kt | 2 - .../magisk/model/events/SnackbarEvent.kt | 1 - .../magisk/model/update/UpdateCheckService.kt | 2 +- .../com/topjohnwu/magisk/ui/MainActivity.kt | 166 +++++++++-- .../com/topjohnwu/magisk/ui/MainViewModel.kt | 2 +- .../magisk/ui/base/MagiskActivity.kt | 260 ------------------ .../magisk/ui/base/MagiskFragment.kt | 85 ------ .../magisk/ui/flash/FlashActivity.kt | 2 +- .../magisk/ui/flash/FlashViewModel.kt | 2 +- .../topjohnwu/magisk/ui/hide/HideViewModel.kt | 4 +- .../magisk/ui/hide/MagiskHideFragment.kt | 2 +- .../topjohnwu/magisk/ui/home/HomeFragment.kt | 7 +- .../topjohnwu/magisk/ui/home/HomeViewModel.kt | 2 +- .../topjohnwu/magisk/ui/log/LogFragment.kt | 4 +- .../topjohnwu/magisk/ui/log/LogViewModel.kt | 4 +- .../magisk/ui/module/ModuleViewModel.kt | 2 +- .../magisk/ui/module/ModulesFragment.kt | 4 +- .../magisk/ui/module/ReposFragment.kt | 4 +- .../magisk/ui/settings/SettingsFragment.kt | 2 +- .../magisk/ui/superuser/SuperuserFragment.kt | 2 +- .../magisk/ui/superuser/SuperuserViewModel.kt | 6 +- .../magisk/ui/surequest/SuRequestActivity.kt | 4 +- .../magisk/ui/surequest/SuRequestViewModel.kt | 2 +- .../view/dialogs/InstallMethodDialog.kt | 2 +- .../view/dialogs/MagiskInstallDialog.kt | 2 +- app/src/main/res/layout/fragment_magisk.xml | 2 +- 41 files changed, 365 insertions(+), 413 deletions(-) rename app/src/main/java/com/topjohnwu/magisk/{ui => }/base/BasePreferenceFragment.kt (98%) rename app/src/main/java/com/topjohnwu/magisk/{model/worker => base}/DelegateWorker.kt (96%) create mode 100644 app/src/main/java/com/topjohnwu/magisk/base/MagiskActivity.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/base/MagiskFragment.kt rename app/src/main/java/com/topjohnwu/magisk/{ => base}/viewmodel/LoadingViewModel.kt (94%) rename app/src/main/java/com/topjohnwu/magisk/{ui/base => base/viewmodel}/MagiskViewModel.kt (93%) rename app/src/main/java/com/topjohnwu/magisk/{ => base}/viewmodel/ObservableViewModel.kt (96%) rename app/src/main/java/com/topjohnwu/magisk/{ => base}/viewmodel/StatefulViewModel.kt (86%) rename app/src/main/java/com/topjohnwu/magisk/{ => base}/viewmodel/TeanityViewModel.kt (95%) delete mode 100644 app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskFragment.kt diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 37a7fea13..c95d10abb 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -29,7 +29,7 @@ } # DelegateWorker --keep,allowobfuscation class * extends com.topjohnwu.magisk.model.worker.DelegateWorker +-keep,allowobfuscation class * extends com.topjohnwu.magisk.base.DelegateWorker # BootSigner -keepclassmembers class com.topjohnwu.signing.BootSigner { *; } diff --git a/app/src/main/java/a/w.java b/app/src/main/java/a/w.java index 17572db75..f852a8968 100644 --- a/app/src/main/java/a/w.java +++ b/app/src/main/java/a/w.java @@ -6,7 +6,7 @@ import androidx.annotation.NonNull; import androidx.work.Worker; import androidx.work.WorkerParameters; -import com.topjohnwu.magisk.model.worker.DelegateWorker; +import com.topjohnwu.magisk.base.DelegateWorker; import java.lang.reflect.ParameterizedType; diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/BasePreferenceFragment.kt b/app/src/main/java/com/topjohnwu/magisk/base/BasePreferenceFragment.kt similarity index 98% rename from app/src/main/java/com/topjohnwu/magisk/ui/base/BasePreferenceFragment.kt rename to app/src/main/java/com/topjohnwu/magisk/base/BasePreferenceFragment.kt index 9747903a7..092be22c6 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/BasePreferenceFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/BasePreferenceFragment.kt @@ -1,4 +1,4 @@ -package com.topjohnwu.magisk.ui.base +package com.topjohnwu.magisk.base import android.annotation.SuppressLint import android.content.SharedPreferences diff --git a/app/src/main/java/com/topjohnwu/magisk/model/worker/DelegateWorker.kt b/app/src/main/java/com/topjohnwu/magisk/base/DelegateWorker.kt similarity index 96% rename from app/src/main/java/com/topjohnwu/magisk/model/worker/DelegateWorker.kt rename to app/src/main/java/com/topjohnwu/magisk/base/DelegateWorker.kt index fe91cdf82..fd5e6e8f2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/worker/DelegateWorker.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/DelegateWorker.kt @@ -1,4 +1,4 @@ -package com.topjohnwu.magisk.model.worker +package com.topjohnwu.magisk.base import android.content.Context import android.net.Network diff --git a/app/src/main/java/com/topjohnwu/magisk/base/MagiskActivity.kt b/app/src/main/java/com/topjohnwu/magisk/base/MagiskActivity.kt new file mode 100644 index 000000000..fe7338334 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/base/MagiskActivity.kt @@ -0,0 +1,112 @@ +package com.topjohnwu.magisk.base + +import android.Manifest +import android.content.Context +import android.content.Intent +import android.content.res.Configuration +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.app.AppCompatDelegate +import androidx.collection.SparseArrayCompat +import androidx.core.net.toUri +import androidx.databinding.DataBindingUtil +import androidx.databinding.ViewDataBinding +import com.karumi.dexter.Dexter +import com.karumi.dexter.MultiplePermissionsReport +import com.karumi.dexter.PermissionToken +import com.karumi.dexter.listener.PermissionRequest +import com.karumi.dexter.listener.multi.MultiplePermissionsListener +import com.topjohnwu.magisk.BR +import com.topjohnwu.magisk.Config +import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel +import com.topjohnwu.magisk.extensions.set +import com.topjohnwu.magisk.model.events.EventHandler +import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder +import com.topjohnwu.magisk.utils.LocaleManager +import com.topjohnwu.magisk.utils.Utils +import com.topjohnwu.magisk.utils.currentLocale + +typealias RequestCallback = MagiskActivity<*, *>.(Int, Intent?) -> Unit + +abstract class MagiskActivity : + AppCompatActivity(), EventHandler { + + protected lateinit var binding: Binding + protected abstract val layoutRes: Int + protected abstract val viewModel: ViewModel + protected open val snackbarView get() = binding.root + protected open val navHostId: Int = 0 + protected open val defaultPosition: Int = 0 + + private val resultCallbacks = SparseArrayCompat() + + init { + val theme = if (Config.darkTheme) { + AppCompatDelegate.MODE_NIGHT_YES + } else { + AppCompatDelegate.MODE_NIGHT_NO + } + AppCompatDelegate.setDefaultNightMode(theme) + } + + override fun applyOverrideConfiguration(config: Configuration?) { + // Force applying our preferred local + config?.setLocale(currentLocale) + super.applyOverrideConfiguration(config) + } + + override fun attachBaseContext(base: Context) { + super.attachBaseContext(LocaleManager.getLocaleContext(base)) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + viewModel.viewEvents.observe(this, viewEventObserver) + + binding = DataBindingUtil.setContentView(this, layoutRes).apply { + setVariable(BR.viewModel, viewModel) + lifecycleOwner = this@MagiskActivity + } + } + + fun openUrl(url: String) = Utils.openLink(this, url.toUri()) + + fun withPermissions(vararg permissions: String, builder: PermissionRequestBuilder.() -> Unit) { + val request = PermissionRequestBuilder().apply(builder).build() + Dexter.withActivity(this) + .withPermissions(*permissions) + .withListener(object : MultiplePermissionsListener { + override fun onPermissionsChecked(report: MultiplePermissionsReport) { + if (report.areAllPermissionsGranted()) { + request.onSuccess() + } else { + request.onFailure() + } + } + + override fun onPermissionRationaleShouldBeShown( + permissions: MutableList, + token: PermissionToken + ) = token.continuePermissionRequest() + }).check() + } + + fun withExternalRW(builder: PermissionRequestBuilder.() -> Unit) { + withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, builder = builder) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + resultCallbacks[requestCode]?.apply { + resultCallbacks.remove(requestCode) + invoke(this@MagiskActivity, resultCode, data) + } + } + + fun startActivityForResult(intent: Intent, requestCode: Int, listener: RequestCallback) { + resultCallbacks[requestCode] = listener + startActivityForResult(intent, requestCode) + } + +} diff --git a/app/src/main/java/com/topjohnwu/magisk/base/MagiskFragment.kt b/app/src/main/java/com/topjohnwu/magisk/base/MagiskFragment.kt new file mode 100644 index 000000000..a08f4a09e --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/base/MagiskFragment.kt @@ -0,0 +1,51 @@ +package com.topjohnwu.magisk.base + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.CallSuper +import androidx.databinding.DataBindingUtil +import androidx.databinding.ViewDataBinding +import androidx.fragment.app.Fragment +import com.topjohnwu.magisk.BR +import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel +import com.topjohnwu.magisk.model.events.EventHandler +import com.topjohnwu.magisk.model.events.ViewEvent +import com.topjohnwu.magisk.ui.MainActivity + +abstract class MagiskFragment : + Fragment(), EventHandler { + + protected val activity get() = requireActivity() as MainActivity + protected lateinit var binding: Binding + protected abstract val layoutRes: Int + protected abstract val viewModel: ViewModel + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + viewModel.viewEvents.observe(this, viewEventObserver) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DataBindingUtil.inflate(inflater, layoutRes, container, false).apply { + setVariable(BR.viewModel, viewModel) + lifecycleOwner = this@MagiskFragment + } + + return binding.root + } + + @CallSuper + override fun onEventDispatched(event: ViewEvent) { + super.onEventDispatched(event) + activity.onEventDispatched(event) + } + + open fun onBackPressed(): Boolean = false + +} diff --git a/app/src/main/java/com/topjohnwu/magisk/viewmodel/LoadingViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/LoadingViewModel.kt similarity index 94% rename from app/src/main/java/com/topjohnwu/magisk/viewmodel/LoadingViewModel.kt rename to app/src/main/java/com/topjohnwu/magisk/base/viewmodel/LoadingViewModel.kt index 4ff9deb62..bd4ac195d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/viewmodel/LoadingViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/LoadingViewModel.kt @@ -1,4 +1,4 @@ -package com.topjohnwu.magisk.viewmodel +package com.topjohnwu.magisk.base.viewmodel import androidx.databinding.Bindable import com.topjohnwu.magisk.BR @@ -13,7 +13,7 @@ abstract class LoadingViewModel(defaultState: State = State.LOADING) : @Deprecated( "Direct access is recommended since 0.2. This access method will be removed in 1.0", - ReplaceWith("state = State.LOADING", "com.topjohnwu.magisk.viewmodel.LoadingViewModel.State"), + ReplaceWith("state = State.LOADING", "com.topjohnwu.magisk.base.viewmodel.LoadingViewModel.State"), DeprecationLevel.WARNING ) fun setLoading() { @@ -22,7 +22,7 @@ abstract class LoadingViewModel(defaultState: State = State.LOADING) : @Deprecated( "Direct access is recommended since 0.2. This access method will be removed in 1.0", - ReplaceWith("state = State.LOADED", "com.topjohnwu.magisk.viewmodel.LoadingViewModel.State"), + ReplaceWith("state = State.LOADED", "com.topjohnwu.magisk.base.viewmodel.LoadingViewModel.State"), DeprecationLevel.WARNING ) fun setLoaded() { @@ -31,7 +31,7 @@ abstract class LoadingViewModel(defaultState: State = State.LOADING) : @Deprecated( "Direct access is recommended since 0.2. This access method will be removed in 1.0", - ReplaceWith("state = State.LOADING_FAILED", "com.topjohnwu.magisk.viewmodel.LoadingViewModel.State"), + ReplaceWith("state = State.LOADING_FAILED", "com.topjohnwu.magisk.base.viewmodel.LoadingViewModel.State"), DeprecationLevel.WARNING ) fun setLoadingFailed() { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/MagiskViewModel.kt similarity index 93% rename from app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt rename to app/src/main/java/com/topjohnwu/magisk/base/viewmodel/MagiskViewModel.kt index 2cee6a32f..cf9c14c75 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/MagiskViewModel.kt @@ -1,4 +1,4 @@ -package com.topjohnwu.magisk.ui.base +package com.topjohnwu.magisk.base.viewmodel import android.app.Activity import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork @@ -9,7 +9,6 @@ import com.topjohnwu.magisk.model.events.BackPressEvent import com.topjohnwu.magisk.model.events.PermissionEvent import com.topjohnwu.magisk.model.events.ViewActionEvent import com.topjohnwu.magisk.utils.KObservableField -import com.topjohnwu.magisk.viewmodel.LoadingViewModel import io.reactivex.Observable import io.reactivex.subjects.PublishSubject diff --git a/app/src/main/java/com/topjohnwu/magisk/viewmodel/ObservableViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/ObservableViewModel.kt similarity index 96% rename from app/src/main/java/com/topjohnwu/magisk/viewmodel/ObservableViewModel.kt rename to app/src/main/java/com/topjohnwu/magisk/base/viewmodel/ObservableViewModel.kt index 1299836d8..17ea6f373 100644 --- a/app/src/main/java/com/topjohnwu/magisk/viewmodel/ObservableViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/ObservableViewModel.kt @@ -1,4 +1,4 @@ -package com.topjohnwu.magisk.viewmodel +package com.topjohnwu.magisk.base.viewmodel import androidx.databinding.Observable import androidx.databinding.PropertyChangeRegistry diff --git a/app/src/main/java/com/topjohnwu/magisk/viewmodel/StatefulViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/StatefulViewModel.kt similarity index 86% rename from app/src/main/java/com/topjohnwu/magisk/viewmodel/StatefulViewModel.kt rename to app/src/main/java/com/topjohnwu/magisk/base/viewmodel/StatefulViewModel.kt index 5bf6229ff..e441c84db 100644 --- a/app/src/main/java/com/topjohnwu/magisk/viewmodel/StatefulViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/StatefulViewModel.kt @@ -1,4 +1,4 @@ -package com.topjohnwu.magisk.viewmodel +package com.topjohnwu.magisk.base.viewmodel abstract class StatefulViewModel>( val defaultState: State diff --git a/app/src/main/java/com/topjohnwu/magisk/viewmodel/TeanityViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/TeanityViewModel.kt similarity index 95% rename from app/src/main/java/com/topjohnwu/magisk/viewmodel/TeanityViewModel.kt rename to app/src/main/java/com/topjohnwu/magisk/base/viewmodel/TeanityViewModel.kt index 799a2305e..4fb10599a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/viewmodel/TeanityViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/TeanityViewModel.kt @@ -1,4 +1,4 @@ -package com.topjohnwu.magisk.viewmodel +package com.topjohnwu.magisk.base.viewmodel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt index 9e36d401d..bf7a0d2a3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt @@ -1,6 +1,5 @@ package com.topjohnwu.magisk.model.entity.recycler -import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.R import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback @@ -12,6 +11,7 @@ import com.topjohnwu.magisk.model.entity.state.IndeterminateState import com.topjohnwu.magisk.model.events.HideProcessEvent import com.topjohnwu.magisk.utils.DiffObservableList import com.topjohnwu.magisk.utils.KObservableField +import com.topjohnwu.magisk.utils.RxBus class HideRvItem(val item: HideAppInfo, targets: List) : ComparableRvItem() { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt index 5006ef115..f6610c363 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt @@ -1,7 +1,6 @@ package com.topjohnwu.magisk.model.entity.recycler import android.graphics.drawable.Drawable -import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.R import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback @@ -11,6 +10,7 @@ import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.events.PolicyEnableEvent import com.topjohnwu.magisk.model.events.PolicyUpdateEvent import com.topjohnwu.magisk.utils.KObservableField +import com.topjohnwu.magisk.utils.RxBus class PolicyRvItem(val item: MagiskPolicy, val icon: Drawable) : ComparableRvItem() { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/EventHandler.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/EventHandler.kt index 3823881cb..fc0b4fc62 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/EventHandler.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/EventHandler.kt @@ -15,4 +15,11 @@ internal interface EventHandler { * the way how you handle them. */ fun onSimpleEventDispatched(event: Int) {} + + val viewEventObserver get() = ViewEventObserver { + onEventDispatched(it) + if (it is SimpleViewEvent) { + onSimpleEventDispatched(it.event) + } + } } diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt index b03814b54..a0dd317e4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt @@ -1,10 +1,10 @@ package com.topjohnwu.magisk.model.events -import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.entity.recycler.HideProcessRvItem import com.topjohnwu.magisk.model.entity.recycler.ModuleRvItem import com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem +import com.topjohnwu.magisk.utils.RxBus class HideProcessEvent(val item: HideProcessRvItem) : RxBus.Event diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/SimpleViewEvent.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/SimpleViewEvent.kt index 8b0b22bdd..4b8c70a87 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/SimpleViewEvent.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/SimpleViewEvent.kt @@ -1,7 +1,5 @@ package com.topjohnwu.magisk.model.events -import com.topjohnwu.magisk.model.events.ViewEvent - class SimpleViewEvent( val event: Int ) : ViewEvent() \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt index 59ee28af2..c60d79835 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt @@ -3,7 +3,6 @@ package com.topjohnwu.magisk.model.events import android.content.Context import androidx.annotation.StringRes import com.google.android.material.snackbar.Snackbar -import com.topjohnwu.magisk.model.events.ViewEvent class SnackbarEvent private constructor( @StringRes private val messageRes: Int, diff --git a/app/src/main/java/com/topjohnwu/magisk/model/update/UpdateCheckService.kt b/app/src/main/java/com/topjohnwu/magisk/model/update/UpdateCheckService.kt index 68c3043a6..17841cd0c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/update/UpdateCheckService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/update/UpdateCheckService.kt @@ -3,9 +3,9 @@ package com.topjohnwu.magisk.model.update import androidx.work.ListenableWorker import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Info +import com.topjohnwu.magisk.base.DelegateWorker import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.extensions.inject -import com.topjohnwu.magisk.model.worker.DelegateWorker import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.superuser.Shell diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt index a4c623faa..3aa93002d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt @@ -4,15 +4,24 @@ import android.content.Intent import android.os.Bundle import androidx.core.view.GravityCompat import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentTransaction +import com.ncapdevi.fragnav.FragNavController +import com.ncapdevi.fragnav.FragNavTransactionOptions import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const.Key.OPEN_SECTION import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.MagiskActivity +import com.topjohnwu.magisk.base.MagiskFragment import com.topjohnwu.magisk.databinding.ActivityMainBinding import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback +import com.topjohnwu.magisk.extensions.snackbar +import com.topjohnwu.magisk.model.events.* +import com.topjohnwu.magisk.model.navigation.MagiskAnimBuilder +import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent import com.topjohnwu.magisk.model.navigation.Navigation -import com.topjohnwu.magisk.ui.base.MagiskActivity +import com.topjohnwu.magisk.model.navigation.Navigator import com.topjohnwu.magisk.ui.hide.MagiskHideFragment import com.topjohnwu.magisk.ui.home.HomeFragment import com.topjohnwu.magisk.ui.log.LogFragment @@ -23,16 +32,24 @@ import com.topjohnwu.magisk.ui.superuser.SuperuserFragment import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.superuser.Shell import org.koin.androidx.viewmodel.ext.android.viewModel +import timber.log.Timber import kotlin.reflect.KClass - -open class MainActivity : MagiskActivity() { +open class MainActivity : MagiskActivity(), Navigator, + FragNavController.RootFragmentListener, FragNavController.TransactionListener { override val layoutRes: Int = R.layout.activity_main override val viewModel: MainViewModel by viewModel() override val navHostId: Int = R.id.main_nav_host override val defaultPosition: Int = 0 + private val navigationController get() = if (navHostId == 0) null else _navigationController + private val _navigationController by lazy { + FragNavController(supportFragmentManager, navHostId) + } + private val isRootFragment get() = + navigationController?.let { it.currentStackIndex != defaultPosition } ?: false + override val baseFragments: List> = listOf( HomeFragment::class, SuperuserFragment::class, @@ -50,6 +67,13 @@ open class MainActivity : MagiskActivity() { } super.onCreate(savedInstanceState) + + navigationController?.apply { + rootFragmentListener = this@MainActivity + transactionListener = this@MainActivity + initialize(defaultPosition, savedInstanceState) + } + checkHideSection() setSupportActionBar(binding.mainInclude.mainToolbar) @@ -64,6 +88,11 @@ open class MainActivity : MagiskActivity() { } } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + navigationController?.onSaveInstanceState(outState) + } + override fun setTitle(title: CharSequence?) { supportActionBar?.title = title } @@ -72,21 +101,27 @@ open class MainActivity : MagiskActivity() { supportActionBar?.setTitle(titleId) } - override fun onTabTransaction(fragment: Fragment?, index: Int) { - val fragmentId = when (fragment) { - is HomeFragment -> R.id.magiskFragment - is SuperuserFragment -> R.id.superuserFragment - is MagiskHideFragment -> R.id.magiskHideFragment - is ModulesFragment -> R.id.modulesFragment - is ReposFragment -> R.id.reposFragment - is LogFragment -> R.id.logFragment - is SettingsFragment -> R.id.settings - else -> return - } - binding.navView.setCheckedItem(fragmentId) - } - override fun onBackPressed() { + val fragment = navigationController?.currentFrag as? MagiskFragment<*, *> + + if (fragment?.onBackPressed() == true) { + return + } + + try { + navigationController?.popFragment() ?: throw UnsupportedOperationException() + } catch (e: UnsupportedOperationException) { + when { + isRootFragment -> { + val options = FragNavTransactionOptions.newBuilder() + .transition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE) + .build() + navigationController?.switchTab(defaultPosition, options) + } + else -> super.onBackPressed() + } + } + if (binding.drawerLayout.isDrawerOpen(binding.navView)) { binding.drawerLayout.closeDrawer(binding.navView) } else { @@ -94,6 +129,23 @@ open class MainActivity : MagiskActivity() { } } + override fun onEventDispatched(event: ViewEvent) { + super.onEventDispatched(event) + when (event) { + is SnackbarEvent -> snackbar(snackbarView, event.message(this), event.length, event.f) + is BackPressEvent -> onBackPressed() + is MagiskNavigationEvent -> navigateTo(event) + is ViewActionEvent -> event.action(this) + is PermissionEvent -> withPermissions(*event.permissions.toTypedArray()) { + onSuccess { event.callback.onNext(true) } + onFailure { + event.callback.onNext(false) + event.callback.onError(SecurityException("User refused permissions")) + } + } + } + } + override fun onSimpleEventDispatched(event: Int) { super.onSimpleEventDispatched(event) when (event) { @@ -116,4 +168,84 @@ open class MainActivity : MagiskActivity() { menu.findItem(R.id.superuserFragment).isVisible = Utils.showSuperUser() } + + private fun FragNavTransactionOptions.Builder.customAnimations(options: MagiskAnimBuilder) = + customAnimations(options.enter, options.exit, options.popEnter, options.popExit).apply { + if (!options.anySet) { + transition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) + } + } + + override val numberOfRootFragments: Int get() = baseFragments.size + + override fun getRootFragment(index: Int) = baseFragments[index].java.newInstance() + + override fun onTabTransaction(fragment: Fragment?, index: Int) { + val fragmentId = when (fragment) { + is HomeFragment -> R.id.magiskFragment + is SuperuserFragment -> R.id.superuserFragment + is MagiskHideFragment -> R.id.magiskHideFragment + is ModulesFragment -> R.id.modulesFragment + is ReposFragment -> R.id.reposFragment + is LogFragment -> R.id.logFragment + is SettingsFragment -> R.id.settings + else -> return + } + binding.navView.setCheckedItem(fragmentId) + } + + override fun navigateTo(event: MagiskNavigationEvent) { + val directions = event.navDirections + + navigationController?.defaultTransactionOptions = FragNavTransactionOptions.newBuilder() + .customAnimations(event.animOptions) + .build() + + navigationController?.currentStack + ?.indexOfFirst { it.javaClass == event.navOptions.popUpTo } + ?.let { if (it == -1) null else it } // invalidate if class is not found + ?.let { if (event.navOptions.inclusive) it + 1 else it } + ?.let { navigationController?.popFragments(it) } + + when (directions.isActivity) { + true -> navigateToActivity(event) + else -> navigateToFragment(event) + } + } + + private fun navigateToActivity(event: MagiskNavigationEvent) { + val destination = event.navDirections.destination?.java ?: let { + Timber.e("Cannot navigate to null destination") + return + } + val options = event.navOptions + + Intent(this, destination) + .putExtras(event.navDirections.args) + .apply { + if (options.singleTop) addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + if (options.clearTask) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + } + .let { startActivity(it) } + } + + private fun navigateToFragment(event: MagiskNavigationEvent) { + val destination = event.navDirections.destination?.java ?: let { + Timber.e("Cannot navigate to null destination") + return + } + + when (val index = baseFragments.indexOfFirst { it.java.name == destination.name }) { + -1 -> destination.newInstance() + .apply { arguments = event.navDirections.args } + .let { navigationController?.pushFragment(it) } + // When it's desired that fragments of same class are put on top of one another edit this + else -> navigationController?.switchTab(index) + } + } + + override fun onFragmentTransaction( + fragment: Fragment?, + transactionType: FragNavController.TransactionType + ) = Unit } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt index 73d8b80c8..4264988d1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt @@ -2,8 +2,8 @@ package com.topjohnwu.magisk.ui import android.view.MenuItem import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel import com.topjohnwu.magisk.model.navigation.Navigation -import com.topjohnwu.magisk.ui.base.MagiskViewModel class MainViewModel : MagiskViewModel() { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt deleted file mode 100644 index 797c99a0f..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt +++ /dev/null @@ -1,260 +0,0 @@ -package com.topjohnwu.magisk.ui.base - -import android.Manifest -import android.content.Context -import android.content.Intent -import android.content.res.Configuration -import android.os.Bundle -import androidx.annotation.CallSuper -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.app.AppCompatDelegate -import androidx.collection.SparseArrayCompat -import androidx.core.net.toUri -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentTransaction -import com.karumi.dexter.Dexter -import com.karumi.dexter.MultiplePermissionsReport -import com.karumi.dexter.PermissionToken -import com.karumi.dexter.listener.PermissionRequest -import com.karumi.dexter.listener.multi.MultiplePermissionsListener -import com.ncapdevi.fragnav.FragNavController -import com.ncapdevi.fragnav.FragNavTransactionOptions -import com.topjohnwu.magisk.model.events.EventHandler -import com.topjohnwu.magisk.BR -import com.topjohnwu.magisk.Config -import com.topjohnwu.magisk.extensions.set -import com.topjohnwu.magisk.extensions.snackbar -import com.topjohnwu.magisk.model.events.* -import com.topjohnwu.magisk.model.navigation.MagiskAnimBuilder -import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent -import com.topjohnwu.magisk.model.navigation.Navigator -import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder -import com.topjohnwu.magisk.utils.LocaleManager -import com.topjohnwu.magisk.utils.Utils -import com.topjohnwu.magisk.utils.currentLocale -import timber.log.Timber -import kotlin.reflect.KClass - -typealias RequestCallback = MagiskActivity<*, *>.(Int, Intent?) -> Unit - -abstract class MagiskActivity : - AppCompatActivity(), FragNavController.RootFragmentListener, EventHandler, - Navigator, FragNavController.TransactionListener { - - protected lateinit var binding: Binding - protected abstract val layoutRes: Int - protected abstract val viewModel: ViewModel - protected open val snackbarView get() = binding.root - protected open val navHostId: Int = 0 - private val viewEventObserver = ViewEventObserver { - onEventDispatched(it) - if (it is SimpleViewEvent) { - onSimpleEventDispatched(it.event) - } - } - - private val resultCallbacks = SparseArrayCompat() - - protected open val defaultPosition: Int = 0 - - private val navigationController get() = if (navHostId == 0) null else _navigationController - private val _navigationController by lazy { - if (navHostId == 0) throw IllegalStateException("Did you forget to override \"navHostId\"?") - FragNavController(supportFragmentManager, navHostId) - } - - private val isRootFragment - get() = navigationController?.let { it.currentStackIndex != defaultPosition } ?: false - - override val numberOfRootFragments: Int get() = baseFragments.size - override val baseFragments: List> = listOf() - - init { - val theme = if (Config.darkTheme) { - AppCompatDelegate.MODE_NIGHT_YES - } else { - AppCompatDelegate.MODE_NIGHT_NO - } - AppCompatDelegate.setDefaultNightMode(theme) - } - - override fun applyOverrideConfiguration(config: Configuration?) { - // Force applying our preferred local - config?.setLocale(currentLocale) - super.applyOverrideConfiguration(config) - } - - override fun attachBaseContext(base: Context) { - super.attachBaseContext(LocaleManager.getLocaleContext(base)) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - binding = DataBindingUtil.setContentView(this, layoutRes).apply { - setVariable(BR.viewModel, viewModel) - lifecycleOwner = this@MagiskActivity - } - - viewModel.viewEvents.observe(this, viewEventObserver) - - navigationController?.apply { - rootFragmentListener = this@MagiskActivity - transactionListener = this@MagiskActivity - initialize(defaultPosition, savedInstanceState) - } - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - navigationController?.onSaveInstanceState(outState) - } - - @CallSuper - override fun onEventDispatched(event: ViewEvent) { - super.onEventDispatched(event) - when (event) { - is SnackbarEvent -> snackbar(snackbarView, event.message(this), event.length, event.f) - is BackPressEvent -> onBackPressed() - is MagiskNavigationEvent -> navigateTo(event) - is ViewActionEvent -> event.action(this) - is PermissionEvent -> withPermissions(*event.permissions.toTypedArray()) { - onSuccess { event.callback.onNext(true) } - onFailure { - event.callback.onNext(false) - event.callback.onError(SecurityException("User refused permissions")) - } - } - } - } - - override fun getRootFragment(index: Int) = baseFragments[index].java.newInstance() - - override fun navigateTo(event: MagiskNavigationEvent) { - val directions = event.navDirections - - navigationController?.defaultTransactionOptions = FragNavTransactionOptions.newBuilder() - .customAnimations(event.animOptions) - .build() - - navigationController?.currentStack - ?.indexOfFirst { it.javaClass == event.navOptions.popUpTo } - ?.let { if (it == -1) null else it } // invalidate if class is not found - ?.let { if (event.navOptions.inclusive) it + 1 else it } - ?.let { navigationController?.popFragments(it) } - - when (directions.isActivity) { - true -> navigateToActivity(event) - else -> navigateToFragment(event) - } - } - - private fun navigateToActivity(event: MagiskNavigationEvent) { - val destination = event.navDirections.destination?.java ?: let { - Timber.e("Cannot navigate to null destination") - return - } - val options = event.navOptions - - Intent(this, destination) - .putExtras(event.navDirections.args) - .apply { - if (options.singleTop) addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) - if (options.clearTask) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) - } - .let { startActivity(it) } - } - - private fun navigateToFragment(event: MagiskNavigationEvent) { - val destination = event.navDirections.destination?.java ?: let { - Timber.e("Cannot navigate to null destination") - return - } - - when (val index = baseFragments.indexOfFirst { it.java.name == destination.name }) { - -1 -> destination.newInstance() - .apply { arguments = event.navDirections.args } - .let { navigationController?.pushFragment(it) } - // When it's desired that fragments of same class are put on top of one another edit this - else -> navigationController?.switchTab(index) - } - } - - override fun onBackPressed() { - val fragment = navigationController?.currentFrag as? MagiskFragment<*, *> - - if (fragment?.onBackPressed() == true) { - return - } - - try { - navigationController?.popFragment() ?: throw UnsupportedOperationException() - } catch (e: UnsupportedOperationException) { - when { - isRootFragment -> { - val options = FragNavTransactionOptions.newBuilder() - .transition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE) - .build() - navigationController?.switchTab(defaultPosition, options) - } - else -> super.onBackPressed() - } - } - } - - override fun onFragmentTransaction( - fragment: Fragment?, - transactionType: FragNavController.TransactionType - ) = Unit - - override fun onTabTransaction(fragment: Fragment?, index: Int) = Unit - - fun openUrl(url: String) = Utils.openLink(this, url.toUri()) - - fun withPermissions(vararg permissions: String, builder: PermissionRequestBuilder.() -> Unit) { - val request = PermissionRequestBuilder().apply(builder).build() - Dexter.withActivity(this) - .withPermissions(*permissions) - .withListener(object : MultiplePermissionsListener { - override fun onPermissionsChecked(report: MultiplePermissionsReport) { - if (report.areAllPermissionsGranted()) { - request.onSuccess() - } else { - request.onFailure() - } - } - - override fun onPermissionRationaleShouldBeShown( - permissions: MutableList, - token: PermissionToken - ) = token.continuePermissionRequest() - }).check() - } - - fun withExternalRW(builder: PermissionRequestBuilder.() -> Unit) { - withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, builder = builder) - } - - private fun FragNavTransactionOptions.Builder.customAnimations(options: MagiskAnimBuilder) = - customAnimations(options.enter, options.exit, options.popEnter, options.popExit).apply { - if (!options.anySet) { - transition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) - } - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - resultCallbacks[requestCode]?.apply { - resultCallbacks.remove(requestCode) - invoke(this@MagiskActivity, resultCode, data) - } - } - - fun startActivityForResult(intent: Intent, requestCode: Int, listener: RequestCallback) { - resultCallbacks[requestCode] = listener - startActivityForResult(intent, requestCode) - } - -} diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskFragment.kt deleted file mode 100644 index 555da437e..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskFragment.kt +++ /dev/null @@ -1,85 +0,0 @@ -package com.topjohnwu.magisk.ui.base - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.annotation.CallSuper -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding -import androidx.fragment.app.Fragment -import androidx.navigation.findNavController -import com.topjohnwu.magisk.BR -import com.topjohnwu.magisk.extensions.snackbar -import com.topjohnwu.magisk.model.events.* -import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent -import com.topjohnwu.magisk.model.navigation.Navigator -import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder -import kotlin.reflect.KClass - -abstract class MagiskFragment : - Fragment(), Navigator, EventHandler { - - protected val activity get() = requireActivity() as MagiskActivity<*, *> - protected lateinit var binding: Binding - protected abstract val layoutRes: Int - protected abstract val viewModel: ViewModel - protected open val snackbarView get() = binding.root - protected val navController get() = binding.root.findNavController() - private val viewEventObserver = ViewEventObserver { - onEventDispatched(it) - if (it is SimpleViewEvent) { - onSimpleEventDispatched(it.event) - } - } - - // We don't need nested fragments - override val baseFragments: List> = listOf() - - override fun navigateTo(event: MagiskNavigationEvent) = activity.navigateTo(event) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - viewModel.viewEvents.observe(this, viewEventObserver) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = DataBindingUtil.inflate(inflater, layoutRes, container, false).apply { - setVariable(BR.viewModel, viewModel) - lifecycleOwner = this@MagiskFragment - } - - return binding.root - } - - @CallSuper - override fun onEventDispatched(event: ViewEvent) { - super.onEventDispatched(event) - when (event) { - is SnackbarEvent -> snackbar(snackbarView, event.message(requireContext()), event.length, event.f) - is BackPressEvent -> activity.onBackPressed() - is MagiskNavigationEvent -> navigateTo(event) - is ViewActionEvent -> event.action(requireActivity()) - is PermissionEvent -> activity.withPermissions(*event.permissions.toTypedArray()) { - onSuccess { event.callback.onNext(true) } - onFailure { - event.callback.onNext(false) - event.callback.onError(SecurityException("User refused permissions")) - } - } - } - } - - fun withPermissions(vararg permissions: String, builder: PermissionRequestBuilder.() -> Unit) { - activity.withPermissions(*permissions, builder = builder) - } - - fun openLink(url: String) = activity.openUrl(url) - - open fun onBackPressed(): Boolean = false - -} diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt index d10deff8a..cbbbc099f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt @@ -9,8 +9,8 @@ import androidx.core.net.toUri import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.MagiskActivity import com.topjohnwu.magisk.databinding.ActivityFlashBinding -import com.topjohnwu.magisk.ui.base.MagiskActivity import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import java.io.File diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt index c51c2883d..d76c44930 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt @@ -11,6 +11,7 @@ import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.* import com.topjohnwu.magisk.model.entity.recycler.ConsoleRvItem @@ -18,7 +19,6 @@ import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.model.flash.FlashResultListener import com.topjohnwu.magisk.model.flash.Flashing import com.topjohnwu.magisk.model.flash.Patching -import com.topjohnwu.magisk.ui.base.MagiskViewModel import com.topjohnwu.magisk.utils.DiffObservableList import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.superuser.Shell diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt index 762db9667..42b5ab774 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt @@ -1,8 +1,8 @@ package com.topjohnwu.magisk.ui.hide import android.content.pm.ApplicationInfo -import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.BR +import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback @@ -13,9 +13,9 @@ import com.topjohnwu.magisk.model.entity.recycler.HideProcessRvItem import com.topjohnwu.magisk.model.entity.recycler.HideRvItem import com.topjohnwu.magisk.model.entity.state.IndeterminateState import com.topjohnwu.magisk.model.events.HideProcessEvent -import com.topjohnwu.magisk.ui.base.MagiskViewModel import com.topjohnwu.magisk.utils.DiffObservableList import com.topjohnwu.magisk.utils.KObservableField +import com.topjohnwu.magisk.utils.RxBus import me.tatarka.bindingcollectionadapter2.OnItemBind import timber.log.Timber diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt index 3c0a14519..08bb96e19 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt @@ -6,8 +6,8 @@ import android.view.MenuItem import android.widget.SearchView import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.MagiskFragment import com.topjohnwu.magisk.databinding.FragmentMagiskHideBinding -import com.topjohnwu.magisk.ui.base.MagiskFragment import org.koin.androidx.viewmodel.ext.android.viewModel class MagiskHideFragment : MagiskFragment(), diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt index 2794205b6..4a72c580e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt @@ -1,19 +1,18 @@ package com.topjohnwu.magisk.ui.home import android.content.Context -import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.MagiskActivity +import com.topjohnwu.magisk.base.MagiskFragment import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.databinding.FragmentMagiskBinding import com.topjohnwu.magisk.extensions.inject import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.writeTo import com.topjohnwu.magisk.model.events.* -import com.topjohnwu.magisk.ui.base.MagiskActivity -import com.topjohnwu.magisk.ui.base.MagiskFragment import com.topjohnwu.magisk.utils.DynamicClassLoader import com.topjohnwu.magisk.utils.SafetyNetHelper import com.topjohnwu.magisk.view.MarkDownWindow @@ -39,7 +38,7 @@ class HomeFragment : MagiskFragment(), override fun onEventDispatched(event: ViewEvent) { super.onEventDispatched(event) when (event) { - is OpenLinkEvent -> openLink(event.url) + is OpenLinkEvent -> activity.openUrl(event.url) is ManagerInstallEvent -> installManager() is MagiskInstallEvent -> installMagisk() is UninstallEvent -> uninstall() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt index 6b6c902f2..34f2024a9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt @@ -2,11 +2,11 @@ package com.topjohnwu.magisk.ui.home import android.content.pm.PackageManager import com.topjohnwu.magisk.* +import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.extensions.* import com.topjohnwu.magisk.model.events.* import com.topjohnwu.magisk.model.observer.Observer -import com.topjohnwu.magisk.ui.base.MagiskViewModel import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.SafetyNetHelper import com.topjohnwu.superuser.Shell diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt index e0fb8aef7..91379308c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt @@ -6,11 +6,11 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View -import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.MagiskFragment import com.topjohnwu.magisk.databinding.FragmentLogBinding import com.topjohnwu.magisk.model.events.PageChangedEvent -import com.topjohnwu.magisk.ui.base.MagiskFragment +import com.topjohnwu.magisk.model.events.ViewEvent import org.koin.androidx.viewmodel.ext.android.viewModel class LogFragment : MagiskFragment() { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt index 6493d5fde..dc6e14613 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt @@ -1,11 +1,11 @@ package com.topjohnwu.magisk.ui.log import android.content.res.Resources -import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel import com.topjohnwu.magisk.data.repository.LogRepository import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback @@ -17,7 +17,7 @@ import com.topjohnwu.magisk.model.entity.recycler.LogItemRvItem import com.topjohnwu.magisk.model.entity.recycler.LogRvItem import com.topjohnwu.magisk.model.entity.recycler.MagiskLogRvItem import com.topjohnwu.magisk.model.events.PageChangedEvent -import com.topjohnwu.magisk.ui.base.MagiskViewModel +import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.utils.DiffObservableList import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.superuser.Shell diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt index 35d74aefd..56c745084 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt @@ -3,6 +3,7 @@ package com.topjohnwu.magisk.ui.module import android.content.res.Resources import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel import com.topjohnwu.magisk.data.database.RepoDao import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.* @@ -14,7 +15,6 @@ import com.topjohnwu.magisk.model.events.InstallModuleEvent import com.topjohnwu.magisk.model.events.OpenChangelogEvent import com.topjohnwu.magisk.model.events.OpenFilePickerEvent import com.topjohnwu.magisk.tasks.RepoUpdater -import com.topjohnwu.magisk.ui.base.MagiskViewModel import com.topjohnwu.magisk.utils.DiffObservableList import com.topjohnwu.magisk.utils.KObservableField import io.reactivex.Single diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt index 7c9799abb..f3f3a9986 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt @@ -8,14 +8,14 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import androidx.recyclerview.widget.RecyclerView -import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.MagiskFragment import com.topjohnwu.magisk.databinding.FragmentModulesBinding import com.topjohnwu.magisk.extensions.reboot import com.topjohnwu.magisk.model.events.OpenFilePickerEvent -import com.topjohnwu.magisk.ui.base.MagiskFragment +import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.ui.flash.FlashActivity import com.topjohnwu.superuser.Shell import org.koin.androidx.viewmodel.ext.android.sharedViewModel diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt index c10d0f567..b9eaae06f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt @@ -6,9 +6,9 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.widget.SearchView -import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.MagiskFragment import com.topjohnwu.magisk.databinding.FragmentReposBinding import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.entity.internal.Configuration @@ -16,7 +16,7 @@ import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.module.Repo import com.topjohnwu.magisk.model.events.InstallModuleEvent import com.topjohnwu.magisk.model.events.OpenChangelogEvent -import com.topjohnwu.magisk.ui.base.MagiskFragment +import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.view.MarkDownWindow import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog import org.koin.androidx.viewmodel.ext.android.sharedViewModel diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt index ac11098f0..8f4d2fced 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt @@ -17,6 +17,7 @@ import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.BasePreferenceFragment import com.topjohnwu.magisk.data.database.RepoDao import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding import com.topjohnwu.magisk.extensions.subscribeK @@ -26,7 +27,6 @@ import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.observer.Observer import com.topjohnwu.magisk.net.Networking -import com.topjohnwu.magisk.ui.base.BasePreferenceFragment import com.topjohnwu.magisk.utils.* import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog import com.topjohnwu.superuser.Shell diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserFragment.kt index 1547fec3e..c9ab0d901 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserFragment.kt @@ -1,8 +1,8 @@ package com.topjohnwu.magisk.ui.superuser import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.MagiskFragment import com.topjohnwu.magisk.databinding.FragmentSuperuserBinding -import com.topjohnwu.magisk.ui.base.MagiskFragment import org.koin.androidx.viewmodel.ext.android.viewModel class SuperuserFragment : diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt index be17f49bb..895a6dbee 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt @@ -2,10 +2,9 @@ package com.topjohnwu.magisk.ui.superuser import android.content.pm.PackageManager import android.content.res.Resources -import com.topjohnwu.magisk.utils.RxBus -import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel import com.topjohnwu.magisk.data.database.PolicyDao import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.applySchedulers @@ -15,9 +14,10 @@ import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem import com.topjohnwu.magisk.model.events.PolicyEnableEvent import com.topjohnwu.magisk.model.events.PolicyUpdateEvent -import com.topjohnwu.magisk.ui.base.MagiskViewModel +import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.utils.DiffObservableList import com.topjohnwu.magisk.utils.FingerprintHelper +import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog import io.reactivex.Single diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt index 3ad04fa96..bd007ce75 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt @@ -5,13 +5,13 @@ import android.os.Build import android.os.Bundle import android.text.TextUtils import android.view.Window -import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.MagiskActivity import com.topjohnwu.magisk.databinding.ActivityRequestBinding import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.events.DieEvent +import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.model.receiver.GeneralReceiver -import com.topjohnwu.magisk.ui.base.MagiskActivity import com.topjohnwu.magisk.utils.SuLogger import org.koin.androidx.viewmodel.ext.android.viewModel diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt index e47412747..e87f9f1f8 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt @@ -12,6 +12,7 @@ import android.text.TextUtils import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel import com.topjohnwu.magisk.data.database.PolicyDao import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback @@ -20,7 +21,6 @@ import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.entity.recycler.SpinnerRvItem import com.topjohnwu.magisk.model.entity.toPolicy import com.topjohnwu.magisk.model.events.DieEvent -import com.topjohnwu.magisk.ui.base.MagiskViewModel import com.topjohnwu.magisk.utils.DiffObservableList import com.topjohnwu.magisk.utils.FingerprintHelper import com.topjohnwu.magisk.utils.KObservableField diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.kt b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.kt index f2be2764f..319e1870a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.kt @@ -7,10 +7,10 @@ import android.widget.Toast import androidx.appcompat.app.AlertDialog import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.base.MagiskActivity import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.DownloadSubject -import com.topjohnwu.magisk.ui.base.MagiskActivity import com.topjohnwu.magisk.utils.Utils internal class InstallMethodDialog(activity: MagiskActivity<*, *>, options: List) : diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.kt b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.kt index 54059b3ab..4c351f55f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.kt @@ -3,7 +3,7 @@ package com.topjohnwu.magisk.view.dialogs import android.net.Uri import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.ui.base.MagiskActivity +import com.topjohnwu.magisk.base.MagiskActivity import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.view.MarkDownWindow import com.topjohnwu.superuser.Shell diff --git a/app/src/main/res/layout/fragment_magisk.xml b/app/src/main/res/layout/fragment_magisk.xml index b762c87cf..399aa75a2 100644 --- a/app/src/main/res/layout/fragment_magisk.xml +++ b/app/src/main/res/layout/fragment_magisk.xml @@ -5,7 +5,7 @@ - + From 6b317f918eab5dd08dcb5ec42e8342cf04ca6d02 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 28 Sep 2019 03:50:11 -0400 Subject: [PATCH 11/50] Rename base class names --- .../base/{MagiskActivity.kt => BaseActivity.kt} | 10 +++++----- .../base/{MagiskFragment.kt => BaseFragment.kt} | 9 ++++----- .../topjohnwu/magisk/base/BasePreferenceFragment.kt | 2 +- .../{MagiskViewModel.kt => BaseViewModel.kt} | 2 +- .../java/com/topjohnwu/magisk/ui/MainActivity.kt | 8 ++++---- .../java/com/topjohnwu/magisk/ui/MainViewModel.kt | 4 ++-- .../com/topjohnwu/magisk/ui/flash/FlashActivity.kt | 4 ++-- .../com/topjohnwu/magisk/ui/flash/FlashViewModel.kt | 4 ++-- .../com/topjohnwu/magisk/ui/hide/HideViewModel.kt | 4 ++-- .../topjohnwu/magisk/ui/hide/MagiskHideFragment.kt | 4 ++-- .../com/topjohnwu/magisk/ui/home/HomeFragment.kt | 8 ++++---- .../com/topjohnwu/magisk/ui/home/HomeViewModel.kt | 4 ++-- .../java/com/topjohnwu/magisk/ui/log/LogFragment.kt | 4 ++-- .../java/com/topjohnwu/magisk/ui/log/LogViewModel.kt | 4 ++-- .../topjohnwu/magisk/ui/module/ModuleViewModel.kt | 4 ++-- .../topjohnwu/magisk/ui/module/ModulesFragment.kt | 4 ++-- .../com/topjohnwu/magisk/ui/module/ReposFragment.kt | 4 ++-- .../magisk/ui/superuser/SuperuserFragment.kt | 4 ++-- .../magisk/ui/superuser/SuperuserViewModel.kt | 4 ++-- .../magisk/ui/surequest/SuRequestActivity.kt | 4 ++-- .../magisk/ui/surequest/SuRequestViewModel.kt | 4 ++-- .../magisk/view/dialogs/InstallMethodDialog.kt | 12 ++++++------ .../magisk/view/dialogs/MagiskInstallDialog.kt | 4 ++-- 23 files changed, 57 insertions(+), 58 deletions(-) rename app/src/main/java/com/topjohnwu/magisk/base/{MagiskActivity.kt => BaseActivity.kt} (92%) rename app/src/main/java/com/topjohnwu/magisk/base/{MagiskFragment.kt => BaseFragment.kt} (81%) rename app/src/main/java/com/topjohnwu/magisk/base/viewmodel/{MagiskViewModel.kt => BaseViewModel.kt} (97%) diff --git a/app/src/main/java/com/topjohnwu/magisk/base/MagiskActivity.kt b/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt similarity index 92% rename from app/src/main/java/com/topjohnwu/magisk/base/MagiskActivity.kt rename to app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt index fe7338334..def4f4aea 100644 --- a/app/src/main/java/com/topjohnwu/magisk/base/MagiskActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt @@ -18,7 +18,7 @@ import com.karumi.dexter.listener.PermissionRequest import com.karumi.dexter.listener.multi.MultiplePermissionsListener import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.Config -import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel +import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.extensions.set import com.topjohnwu.magisk.model.events.EventHandler import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder @@ -26,9 +26,9 @@ import com.topjohnwu.magisk.utils.LocaleManager import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.currentLocale -typealias RequestCallback = MagiskActivity<*, *>.(Int, Intent?) -> Unit +typealias RequestCallback = BaseActivity<*, *>.(Int, Intent?) -> Unit -abstract class MagiskActivity : +abstract class BaseActivity : AppCompatActivity(), EventHandler { protected lateinit var binding: Binding @@ -66,7 +66,7 @@ abstract class MagiskActivity(this, layoutRes).apply { setVariable(BR.viewModel, viewModel) - lifecycleOwner = this@MagiskActivity + lifecycleOwner = this@BaseActivity } } @@ -100,7 +100,7 @@ abstract class MagiskActivity : +abstract class BaseFragment : Fragment(), EventHandler { - protected val activity get() = requireActivity() as MainActivity + protected val activity get() = requireActivity() as BaseActivity<*, *> protected lateinit var binding: Binding protected abstract val layoutRes: Int protected abstract val viewModel: ViewModel @@ -34,7 +33,7 @@ abstract class MagiskFragment(inflater, layoutRes, container, false).apply { setVariable(BR.viewModel, viewModel) - lifecycleOwner = this@MagiskFragment + lifecycleOwner = this@BaseFragment } return binding.root diff --git a/app/src/main/java/com/topjohnwu/magisk/base/BasePreferenceFragment.kt b/app/src/main/java/com/topjohnwu/magisk/base/BasePreferenceFragment.kt index 092be22c6..01aa437c0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/base/BasePreferenceFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/BasePreferenceFragment.kt @@ -14,7 +14,7 @@ abstract class BasePreferenceFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener { protected val prefs: SharedPreferences by inject() - protected val activity get() = requireActivity() as MagiskActivity<*, *> + protected val activity get() = requireActivity() as BaseActivity<*, *> override fun onCreateView( inflater: LayoutInflater, diff --git a/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/MagiskViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/BaseViewModel.kt similarity index 97% rename from app/src/main/java/com/topjohnwu/magisk/base/viewmodel/MagiskViewModel.kt rename to app/src/main/java/com/topjohnwu/magisk/base/viewmodel/BaseViewModel.kt index cf9c14c75..5c065c73b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/MagiskViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/BaseViewModel.kt @@ -13,7 +13,7 @@ import io.reactivex.Observable import io.reactivex.subjects.PublishSubject -abstract class MagiskViewModel( +abstract class BaseViewModel( initialState: State = State.LOADING ) : LoadingViewModel(initialState) { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt index 3aa93002d..9f442f60d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt @@ -12,8 +12,8 @@ import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const.Key.OPEN_SECTION import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.MagiskActivity -import com.topjohnwu.magisk.base.MagiskFragment +import com.topjohnwu.magisk.base.BaseActivity +import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.databinding.ActivityMainBinding import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback import com.topjohnwu.magisk.extensions.snackbar @@ -35,7 +35,7 @@ import org.koin.androidx.viewmodel.ext.android.viewModel import timber.log.Timber import kotlin.reflect.KClass -open class MainActivity : MagiskActivity(), Navigator, +open class MainActivity : BaseActivity(), Navigator, FragNavController.RootFragmentListener, FragNavController.TransactionListener { override val layoutRes: Int = R.layout.activity_main @@ -102,7 +102,7 @@ open class MainActivity : MagiskActivity(), } override fun onBackPressed() { - val fragment = navigationController?.currentFrag as? MagiskFragment<*, *> + val fragment = navigationController?.currentFrag as? BaseFragment<*, *> if (fragment?.onBackPressed() == true) { return diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt index 4264988d1..35688183f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt @@ -2,11 +2,11 @@ package com.topjohnwu.magisk.ui import android.view.MenuItem import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel +import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.model.navigation.Navigation -class MainViewModel : MagiskViewModel() { +class MainViewModel : BaseViewModel() { fun navPressed() = Navigation.Main.OPEN_NAV.publish() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt index cbbbc099f..79e573dca 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt @@ -9,13 +9,13 @@ import androidx.core.net.toUri import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.MagiskActivity +import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.databinding.ActivityFlashBinding import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import java.io.File -open class FlashActivity : MagiskActivity() { +open class FlashActivity : BaseActivity() { override val layoutRes: Int = R.layout.activity_flash override val viewModel: FlashViewModel by viewModel { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt index d76c44930..e2ead2b3b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt @@ -11,7 +11,7 @@ import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel +import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.* import com.topjohnwu.magisk.model.entity.recycler.ConsoleRvItem @@ -31,7 +31,7 @@ class FlashViewModel( installer: Uri, uri: Uri, private val resources: Resources -) : MagiskViewModel(), FlashResultListener { +) : BaseViewModel(), FlashResultListener { val canShowReboot = Shell.rootAccess() val showRestartTitle = KObservableField(false) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt index 42b5ab774..8e09a010f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt @@ -2,7 +2,7 @@ package com.topjohnwu.magisk.ui.hide import android.content.pm.ApplicationInfo import com.topjohnwu.magisk.BR -import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel +import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback @@ -22,7 +22,7 @@ import timber.log.Timber class HideViewModel( private val magiskRepo: MagiskRepository, rxBus: RxBus -) : MagiskViewModel() { +) : BaseViewModel() { val query = KObservableField("") val isShowSystem = KObservableField(false) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt index 08bb96e19..f9d4c67f3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt @@ -6,11 +6,11 @@ import android.view.MenuItem import android.widget.SearchView import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.MagiskFragment +import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.databinding.FragmentMagiskHideBinding import org.koin.androidx.viewmodel.ext.android.viewModel -class MagiskHideFragment : MagiskFragment(), +class MagiskHideFragment : BaseFragment(), SearchView.OnQueryTextListener { override val layoutRes: Int = R.layout.fragment_magisk_hide diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt index 4a72c580e..bc17b8368 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt @@ -5,8 +5,8 @@ import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.MagiskActivity -import com.topjohnwu.magisk.base.MagiskFragment +import com.topjohnwu.magisk.base.BaseActivity +import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.databinding.FragmentMagiskBinding import com.topjohnwu.magisk.extensions.inject @@ -24,7 +24,7 @@ import org.koin.androidx.viewmodel.ext.android.viewModel import java.io.File import java.lang.reflect.InvocationHandler -class HomeFragment : MagiskFragment(), +class HomeFragment : BaseFragment(), SafetyNetHelper.Callback { override val layoutRes: Int = R.layout.fragment_magisk @@ -61,7 +61,7 @@ class HomeFragment : MagiskFragment(), return } - MagiskInstallDialog(requireActivity() as MagiskActivity<*, *>).show() + MagiskInstallDialog(requireActivity() as BaseActivity<*, *>).show() } private fun installManager() = ManagerInstallDialog(requireActivity()).show() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt index 34f2024a9..1f1cdae18 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt @@ -2,7 +2,7 @@ package com.topjohnwu.magisk.ui.home import android.content.pm.PackageManager import com.topjohnwu.magisk.* -import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel +import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.extensions.* import com.topjohnwu.magisk.model.events.* @@ -25,7 +25,7 @@ enum class MagiskItem { class HomeViewModel( private val magiskRepo: MagiskRepository -) : MagiskViewModel(State.LOADED) { +) : BaseViewModel(State.LOADED) { val hasGMS = runCatching { get().getPackageInfo("com.google.android.gms", 0); true diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt index 91379308c..24f0e1db2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt @@ -7,13 +7,13 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.MagiskFragment +import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.databinding.FragmentLogBinding import com.topjohnwu.magisk.model.events.PageChangedEvent import com.topjohnwu.magisk.model.events.ViewEvent import org.koin.androidx.viewmodel.ext.android.viewModel -class LogFragment : MagiskFragment() { +class LogFragment : BaseFragment() { override val layoutRes: Int = R.layout.fragment_log override val viewModel: LogViewModel by viewModel() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt index dc6e14613..21beec611 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt @@ -5,7 +5,7 @@ import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel +import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.data.repository.LogRepository import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback @@ -30,7 +30,7 @@ import java.util.* class LogViewModel( private val resources: Resources, private val logRepo: LogRepository -) : MagiskViewModel(), BindingViewPagerAdapter.PageTitles> { +) : BaseViewModel(), BindingViewPagerAdapter.PageTitles> { val itemsAdapter = BindingAdapter() val items = DiffObservableList(ComparableRvItem.callback) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt index 56c745084..1ac4ed62b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt @@ -3,7 +3,7 @@ package com.topjohnwu.magisk.ui.module import android.content.res.Resources import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel +import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.data.database.RepoDao import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.* @@ -25,7 +25,7 @@ class ModuleViewModel( private val resources: Resources, private val repoUpdater: RepoUpdater, private val repoDB: RepoDao -) : MagiskViewModel() { +) : BaseViewModel() { val query = KObservableField("") diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt index f3f3a9986..fa57d6605 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt @@ -11,7 +11,7 @@ import androidx.recyclerview.widget.RecyclerView import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.MagiskFragment +import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.databinding.FragmentModulesBinding import com.topjohnwu.magisk.extensions.reboot import com.topjohnwu.magisk.model.events.OpenFilePickerEvent @@ -20,7 +20,7 @@ import com.topjohnwu.magisk.ui.flash.FlashActivity import com.topjohnwu.superuser.Shell import org.koin.androidx.viewmodel.ext.android.sharedViewModel -class ModulesFragment : MagiskFragment() { +class ModulesFragment : BaseFragment() { override val layoutRes: Int = R.layout.fragment_modules override val viewModel: ModuleViewModel by sharedViewModel() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt index b9eaae06f..70ebb78c5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt @@ -8,7 +8,7 @@ import android.view.MenuItem import android.widget.SearchView import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.MagiskFragment +import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.databinding.FragmentReposBinding import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.entity.internal.Configuration @@ -21,7 +21,7 @@ import com.topjohnwu.magisk.view.MarkDownWindow import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog import org.koin.androidx.viewmodel.ext.android.sharedViewModel -class ReposFragment : MagiskFragment(), +class ReposFragment : BaseFragment(), SearchView.OnQueryTextListener { override val layoutRes: Int = R.layout.fragment_repos diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserFragment.kt index c9ab0d901..00386b01e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserFragment.kt @@ -1,12 +1,12 @@ package com.topjohnwu.magisk.ui.superuser import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.MagiskFragment +import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.databinding.FragmentSuperuserBinding import org.koin.androidx.viewmodel.ext.android.viewModel class SuperuserFragment : - MagiskFragment() { + BaseFragment() { override val layoutRes: Int = R.layout.fragment_superuser override val viewModel: SuperuserViewModel by viewModel() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt index 895a6dbee..adbae5577 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt @@ -4,7 +4,7 @@ import android.content.pm.PackageManager import android.content.res.Resources import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel +import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.data.database.PolicyDao import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.applySchedulers @@ -29,7 +29,7 @@ class SuperuserViewModel( private val packageManager: PackageManager, private val resources: Resources, rxBus: RxBus -) : MagiskViewModel() { +) : BaseViewModel() { val items = DiffObservableList(ComparableRvItem.callback) val itemBinding = ItemBinding.of> { itemBinding, _, item -> diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt index bd007ce75..2fb93eeea 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt @@ -6,7 +6,7 @@ import android.os.Bundle import android.text.TextUtils import android.view.Window import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.MagiskActivity +import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.databinding.ActivityRequestBinding import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.events.DieEvent @@ -15,7 +15,7 @@ import com.topjohnwu.magisk.model.receiver.GeneralReceiver import com.topjohnwu.magisk.utils.SuLogger import org.koin.androidx.viewmodel.ext.android.viewModel -open class SuRequestActivity : MagiskActivity() { +open class SuRequestActivity : BaseActivity() { override val layoutRes: Int = R.layout.activity_request override val viewModel: SuRequestViewModel by viewModel() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt index e87f9f1f8..0475b2104 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt @@ -12,7 +12,7 @@ import android.text.TextUtils import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.viewmodel.MagiskViewModel +import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.data.database.PolicyDao import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback @@ -36,7 +36,7 @@ class SuRequestViewModel( private val policyDB: PolicyDao, private val timeoutPrefs: SharedPreferences, private val resources: Resources -) : MagiskViewModel() { +) : BaseViewModel() { val icon = KObservableField(null) val title = KObservableField("") diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.kt b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.kt index 319e1870a..4d0e0c09a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.kt @@ -7,13 +7,13 @@ import android.widget.Toast import androidx.appcompat.app.AlertDialog import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.MagiskActivity +import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.utils.Utils -internal class InstallMethodDialog(activity: MagiskActivity<*, *>, options: List) : +internal class InstallMethodDialog(activity: BaseActivity<*, *>, options: List) : AlertDialog.Builder(activity) { init { @@ -28,11 +28,11 @@ internal class InstallMethodDialog(activity: MagiskActivity<*, *>, options: List } } - private fun flash(activity: MagiskActivity<*, *>) = DownloadService(activity) { + private fun flash(activity: BaseActivity<*, *>) = DownloadService(activity) { subject = DownloadSubject.Magisk(Configuration.Flash.Primary) } - private fun patchBoot(activity: MagiskActivity<*, *>) = activity.withExternalRW { + private fun patchBoot(activity: BaseActivity<*, *>) = activity.withExternalRW { onSuccess { Utils.toast(R.string.patch_file_msg, Toast.LENGTH_LONG) val intent = Intent(Intent.ACTION_GET_CONTENT) @@ -49,7 +49,7 @@ internal class InstallMethodDialog(activity: MagiskActivity<*, *>, options: List } } - private fun downloadOnly(activity: MagiskActivity<*, *>) = activity.withExternalRW { + private fun downloadOnly(activity: BaseActivity<*, *>) = activity.withExternalRW { onSuccess { DownloadService(activity) { subject = DownloadSubject.Magisk(Configuration.Download) @@ -57,7 +57,7 @@ internal class InstallMethodDialog(activity: MagiskActivity<*, *>, options: List } } - private fun installInactiveSlot(activity: MagiskActivity<*, *>) { + private fun installInactiveSlot(activity: BaseActivity<*, *>) { CustomAlertDialog(activity) .setTitle(R.string.warning) .setMessage(R.string.install_inactive_slot_msg) diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.kt b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.kt index 4c351f55f..2f83b6f98 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.kt @@ -3,14 +3,14 @@ package com.topjohnwu.magisk.view.dialogs import android.net.Uri import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.base.MagiskActivity +import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.view.MarkDownWindow import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.ShellUtils import java.util.* -class MagiskInstallDialog(a: MagiskActivity<*, *>) : CustomAlertDialog(a) { +class MagiskInstallDialog(a: BaseActivity<*, *>) : CustomAlertDialog(a) { init { val filename = "Magisk v${Info.remote.magisk.version}" + "(${Info.remote.magisk.versionCode})" From 6a10cc9c558479a08729bbfdeddb63724addab84 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 28 Sep 2019 04:23:21 -0400 Subject: [PATCH 12/50] Remove dependency Dexter --- app/build.gradle | 1 - .../com/topjohnwu/magisk/base/BaseActivity.kt | 57 ++++++++++++------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 348df90e3..8485a5d61 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -62,7 +62,6 @@ dependencies { implementation 'com.jakewharton.timber:timber:4.7.1' implementation 'com.ncapdevi:frag-nav:3.2.0' implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.6' - implementation 'com.karumi:dexter:6.0.0' implementation "io.reactivex.rxjava2:rxjava:2.2.12" implementation "io.reactivex.rxjava2:rxkotlin:2.4.0" diff --git a/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt b/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt index def4f4aea..e9c049ff2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt @@ -3,19 +3,17 @@ package com.topjohnwu.magisk.base import android.Manifest import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.content.res.Configuration import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.collection.SparseArrayCompat +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import androidx.core.net.toUri import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding -import com.karumi.dexter.Dexter -import com.karumi.dexter.MultiplePermissionsReport -import com.karumi.dexter.PermissionToken -import com.karumi.dexter.listener.PermissionRequest -import com.karumi.dexter.listener.multi.MultiplePermissionsListener import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.base.viewmodel.BaseViewModel @@ -25,6 +23,7 @@ import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder import com.topjohnwu.magisk.utils.LocaleManager import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.currentLocale +import kotlin.random.Random typealias RequestCallback = BaseActivity<*, *>.(Int, Intent?) -> Unit @@ -38,7 +37,7 @@ abstract class BaseActivity() + private val resultCallbacks by lazy { SparseArrayCompat() } init { val theme = if (Config.darkTheme) { @@ -74,22 +73,38 @@ abstract class BaseActivity Unit) { val request = PermissionRequestBuilder().apply(builder).build() - Dexter.withActivity(this) - .withPermissions(*permissions) - .withListener(object : MultiplePermissionsListener { - override fun onPermissionsChecked(report: MultiplePermissionsReport) { - if (report.areAllPermissionsGranted()) { - request.onSuccess() - } else { - request.onFailure() - } - } + val ungranted = permissions.filter { + ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED + } + + if (ungranted.isEmpty()) { + request.onSuccess() + } else { + val requestCode = Random.nextInt(256, 512) + resultCallbacks[requestCode] = { result, _ -> + if (result > 0) + request.onSuccess() + else + request.onFailure() + } + ActivityCompat.requestPermissions(this, ungranted.toTypedArray(), requestCode) + } + } + + override fun onRequestPermissionsResult( + requestCode: Int, permissions: Array, grantResults: IntArray) { + var success = true + for (res in grantResults) { + if (res != PackageManager.PERMISSION_GRANTED) { + success = false + break + } + } + resultCallbacks[requestCode]?.apply { + resultCallbacks.remove(requestCode) + invoke(this@BaseActivity, if (success) 1 else -1, null) + } - override fun onPermissionRationaleShouldBeShown( - permissions: MutableList, - token: PermissionToken - ) = token.continuePermissionRequest() - }).check() } fun withExternalRW(builder: PermissionRequestBuilder.() -> Unit) { From 5c5625911d8b26c479fb149b262ed50ad4322239 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 28 Sep 2019 05:01:25 -0400 Subject: [PATCH 13/50] Fix back button behavior --- .../com/topjohnwu/magisk/ui/MainActivity.kt | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt index 9f442f60d..8810447af 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt @@ -43,12 +43,11 @@ open class MainActivity : BaseActivity(), Na override val navHostId: Int = R.id.main_nav_host override val defaultPosition: Int = 0 - private val navigationController get() = if (navHostId == 0) null else _navigationController - private val _navigationController by lazy { + private val navigationController by lazy { FragNavController(supportFragmentManager, navHostId) } private val isRootFragment get() = - navigationController?.let { it.currentStackIndex != defaultPosition } ?: false + navigationController.currentStackIndex != defaultPosition override val baseFragments: List> = listOf( HomeFragment::class, @@ -68,7 +67,7 @@ open class MainActivity : BaseActivity(), Na super.onCreate(savedInstanceState) - navigationController?.apply { + navigationController.apply { rootFragmentListener = this@MainActivity transactionListener = this@MainActivity initialize(defaultPosition, savedInstanceState) @@ -90,7 +89,7 @@ open class MainActivity : BaseActivity(), Na override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - navigationController?.onSaveInstanceState(outState) + navigationController.onSaveInstanceState(outState) } override fun setTitle(title: CharSequence?) { @@ -102,30 +101,28 @@ open class MainActivity : BaseActivity(), Na } override fun onBackPressed() { - val fragment = navigationController?.currentFrag as? BaseFragment<*, *> - - if (fragment?.onBackPressed() == true) { - return - } - - try { - navigationController?.popFragment() ?: throw UnsupportedOperationException() - } catch (e: UnsupportedOperationException) { - when { - isRootFragment -> { - val options = FragNavTransactionOptions.newBuilder() - .transition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE) - .build() - navigationController?.switchTab(defaultPosition, options) - } - else -> super.onBackPressed() - } - } - if (binding.drawerLayout.isDrawerOpen(binding.navView)) { binding.drawerLayout.closeDrawer(binding.navView) } else { - super.onBackPressed() + val fragment = navigationController.currentFrag as? BaseFragment<*, *> + + if (fragment?.onBackPressed() == true) { + return + } + + try { + navigationController.popFragment() + } catch (e: UnsupportedOperationException) { + when { + isRootFragment -> { + val options = FragNavTransactionOptions.newBuilder() + .transition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE) + .build() + navigationController.switchTab(defaultPosition, options) + } + else -> super.onBackPressed() + } + } } } @@ -197,15 +194,15 @@ open class MainActivity : BaseActivity(), Na override fun navigateTo(event: MagiskNavigationEvent) { val directions = event.navDirections - navigationController?.defaultTransactionOptions = FragNavTransactionOptions.newBuilder() + navigationController.defaultTransactionOptions = FragNavTransactionOptions.newBuilder() .customAnimations(event.animOptions) .build() - navigationController?.currentStack + navigationController.currentStack ?.indexOfFirst { it.javaClass == event.navOptions.popUpTo } ?.let { if (it == -1) null else it } // invalidate if class is not found ?.let { if (event.navOptions.inclusive) it + 1 else it } - ?.let { navigationController?.popFragments(it) } + ?.let { navigationController.popFragments(it) } when (directions.isActivity) { true -> navigateToActivity(event) @@ -238,9 +235,9 @@ open class MainActivity : BaseActivity(), Na when (val index = baseFragments.indexOfFirst { it.java.name == destination.name }) { -1 -> destination.newInstance() .apply { arguments = event.navDirections.args } - .let { navigationController?.pushFragment(it) } + .let { navigationController.pushFragment(it) } // When it's desired that fragments of same class are put on top of one another edit this - else -> navigationController?.switchTab(index) + else -> navigationController.switchTab(index) } } From c4356171b33e8ad3426346475fce231a90a70b95 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 28 Sep 2019 05:01:51 -0400 Subject: [PATCH 14/50] Update dependencies block --- app/build.gradle | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8485a5d61..b20bfe555 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -70,8 +70,9 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${vKotlin}" def vBAdapt = '3.1.1' - implementation "me.tatarka.bindingcollectionadapter2:bindingcollectionadapter:${vBAdapt}" - implementation "me.tatarka.bindingcollectionadapter2:bindingcollectionadapter-recyclerview:${vBAdapt}" + def bindingAdapter = 'me.tatarka.bindingcollectionadapter2:bindingcollectionadapter' + implementation "${bindingAdapter}:${vBAdapt}" + implementation "${bindingAdapter}-recyclerview:${vBAdapt}" def vMarkwon = '3.1.0' implementation "ru.noties.markwon:core:${vMarkwon}" @@ -118,7 +119,7 @@ dependencies { implementation "androidx.navigation:navigation-ui-ktx:$vNav" implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.browser:browser:1.0.0' + implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02' implementation 'androidx.preference:preference:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.1.0-beta04' implementation 'androidx.cardview:cardview:1.0.0' From d3f49334e299466b83643516f62920a8b05f64d5 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 28 Sep 2019 12:17:34 -0400 Subject: [PATCH 15/50] Move function as extension --- .../java/com/topjohnwu/magisk/base/BaseActivity.kt | 12 ++++-------- .../java/com/topjohnwu/magisk/extensions/XAndroid.kt | 6 +++++- .../com/topjohnwu/magisk/ui/home/HomeFragment.kt | 1 + 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt b/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt index e9c049ff2..04dad084f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt @@ -11,7 +11,6 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.collection.SparseArrayCompat import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat -import androidx.core.net.toUri import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import com.topjohnwu.magisk.BR @@ -21,7 +20,6 @@ import com.topjohnwu.magisk.extensions.set import com.topjohnwu.magisk.model.events.EventHandler import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder import com.topjohnwu.magisk.utils.LocaleManager -import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.currentLocale import kotlin.random.Random @@ -69,8 +67,6 @@ abstract class BaseActivity Unit) { val request = PermissionRequestBuilder().apply(builder).build() val ungranted = permissions.filter { @@ -91,6 +87,10 @@ abstract class BaseActivity Unit) { + withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, builder = builder) + } + override fun onRequestPermissionsResult( requestCode: Int, permissions: Array, grantResults: IntArray) { var success = true @@ -107,10 +107,6 @@ abstract class BaseActivity Unit) { - withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, builder = builder) - } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) resultCallbacks[requestCode]?.apply { diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt index 3c669600a..d8d5802f3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt @@ -17,7 +17,9 @@ import android.view.View import androidx.annotation.ColorRes import androidx.annotation.DrawableRes import androidx.core.content.ContextCompat +import androidx.core.net.toUri import com.topjohnwu.magisk.utils.FileProvider +import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.currentLocale import java.io.File import java.io.FileNotFoundException @@ -152,4 +154,6 @@ fun Context.startEndToLeftRight(start: Int, end: Int): Pair { return end to start } return start to end -} \ No newline at end of file +} + +fun Context.openUrl(url: String) = Utils.openLink(this, url.toUri()) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt index bc17b8368..a35207f61 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt @@ -10,6 +10,7 @@ import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.databinding.FragmentMagiskBinding import com.topjohnwu.magisk.extensions.inject +import com.topjohnwu.magisk.extensions.openUrl import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.writeTo import com.topjohnwu.magisk.model.events.* From 6352fbb3b28864870e56465ebfc83ca7b188078b Mon Sep 17 00:00:00 2001 From: Viktor De Pasquale Date: Sun, 29 Sep 2019 12:54:53 +0200 Subject: [PATCH 16/50] Added additional sorting for installed modules --- .../java/com/topjohnwu/magisk/model/entity/module/Module.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/module/Module.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/module/Module.kt index 8b7d5fa3b..f71e4c71d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/module/Module.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/module/Module.kt @@ -48,7 +48,7 @@ class Module(path: String) : BaseModule() { } if (name.isEmpty()) { - name = id; + name = id } } @@ -65,7 +65,7 @@ class Module(path: String) : BaseModule() { val module = Module(Const.MAGISK_PATH + "/" + file.name) moduleList.add(module) } - return moduleList + return moduleList.sortedBy { it.name } } } } From 5b7ddbbb012ad5cbe3557ff13067a18a32795692 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 30 Sep 2019 15:32:28 -0400 Subject: [PATCH 17/50] Fix status report UI --- .../magisk/base/viewmodel/BaseViewModel.kt | 2 +- .../topjohnwu/magisk/ui/home/HomeViewModel.kt | 52 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/BaseViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/BaseViewModel.kt index 5c065c73b..a9acc6ebb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/BaseViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/BaseViewModel.kt @@ -17,7 +17,7 @@ abstract class BaseViewModel( initialState: State = State.LOADING ) : LoadingViewModel(initialState) { - val isConnected = KObservableField(true) + val isConnected = KObservableField(false) init { ReactiveNetwork.observeNetworkConnectivity(get()) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt index 1f1cdae18..fd5da72b0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt @@ -10,13 +10,14 @@ import com.topjohnwu.magisk.model.observer.Observer import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.SafetyNetHelper import com.topjohnwu.superuser.Shell +import io.reactivex.Completable enum class SafetyNetState { LOADING, PASS, FAILED, IDLE } enum class MagiskState { - NO_ROOT, NOT_INSTALLED, UP_TO_DATE, OBSOLETE, LOADING + NOT_INSTALLED, UP_TO_DATE, OBSOLETE, LOADING } enum class MagiskItem { @@ -37,13 +38,9 @@ class HomeViewModel( val isKeepVerity = KObservableField(Info.keepVerity) val isRecovery = KObservableField(Info.recovery) - private val _magiskState = KObservableField(MagiskState.LOADING) - val magiskState = Observer(_magiskState, isConnected) { - if (isConnected.value) _magiskState.value else MagiskState.UP_TO_DATE - } + val magiskState = KObservableField(MagiskState.LOADING) val magiskStateText = Observer(magiskState) { when (magiskState.value) { - MagiskState.NO_ROOT -> TODO() MagiskState.NOT_INSTALLED -> R.string.magisk_version_error.res() MagiskState.UP_TO_DATE -> R.string.magisk_up_to_date.res() MagiskState.LOADING -> R.string.checking_for_updates.res() @@ -65,7 +62,6 @@ class HomeViewModel( } val managerStateText = Observer(managerState) { when (managerState.value) { - MagiskState.NO_ROOT -> "wtf" MagiskState.NOT_INSTALLED -> R.string.invalid_update_channel.res() MagiskState.UP_TO_DATE -> R.string.manager_up_to_date.res() MagiskState.LOADING -> R.string.checking_for_updates.res() @@ -175,24 +171,28 @@ class HomeViewModel( } fun refresh() { - refreshVersions() - - magiskRepo.fetchUpdate() - .applyViewModel(this) - .doOnSubscribeUi { - _magiskState.value = MagiskState.LOADING - _managerState.value = MagiskState.LOADING - ctsState.value = SafetyNetState.IDLE - basicIntegrityState.value = SafetyNetState.IDLE - safetyNetTitle.value = R.string.safetyNet_check_text - } - .subscribeK { - updateSelf() - ensureEnv() - refreshVersions() - } - hasRoot.value = Shell.rootAccess() + + val fetchUpdate = if (isConnected.value) + magiskRepo.fetchUpdate().ignoreElement() + else + Completable.complete() + + Completable.fromAction { + Info.loadMagiskInfo() + }.andThen(fetchUpdate) + .applyViewModel(this) + .doOnSubscribeUi { + magiskState.value = MagiskState.LOADING + _managerState.value = MagiskState.LOADING + ctsState.value = SafetyNetState.IDLE + basicIntegrityState.value = SafetyNetState.IDLE + safetyNetTitle.value = R.string.safetyNet_check_text + }.subscribeK { + updateSelf() + ensureEnv() + refreshVersions() + } } private fun refreshVersions() { @@ -207,7 +207,7 @@ class HomeViewModel( } private fun updateSelf() { - _magiskState.value = when (Info.magiskVersionCode) { + magiskState.value = when (Info.magiskVersionCode) { in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED !in Info.remote.magisk.versionCode..Int.MAX_VALUE -> MagiskState.OBSOLETE else -> MagiskState.UP_TO_DATE @@ -228,7 +228,7 @@ class HomeViewModel( private fun ensureEnv() { val invalidStates = - listOf(MagiskState.NOT_INSTALLED, MagiskState.NO_ROOT, MagiskState.LOADING) + listOf(MagiskState.NOT_INSTALLED, MagiskState.LOADING) // Don't bother checking env when magisk is not installed, loading or already has been shown if (invalidStates.any { it == magiskState.value } || shownDialog) return From abbd2e6b7204468d446faf8ac5b9e03fdfc7e191 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 5 Oct 2019 17:02:08 -0400 Subject: [PATCH 18/50] Update AS --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4d37e4df7..d80324468 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { maven { url 'https://kotlin.bintray.com/kotlinx' } } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.5.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${vKotlin}" From 21099eabfac2c652a503d01df61df4b8faecef39 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 5 Oct 2019 17:24:53 -0400 Subject: [PATCH 19/50] Small changes in DTB code --- native/jni/magiskboot/dtb.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/native/jni/magiskboot/dtb.cpp b/native/jni/magiskboot/dtb.cpp index 5755c9862..0c2be07fd 100644 --- a/native/jni/magiskboot/dtb.cpp +++ b/native/jni/magiskboot/dtb.cpp @@ -116,6 +116,7 @@ static void dtb_print(const char *file, bool fstab) { print_node(fdt); } ++dtb_num; + i += fdt_totalsize(fdt) - 1; } } fprintf(stderr, "\n"); @@ -140,6 +141,8 @@ static void dtb_patch(const char *in, const char *out) { memcpy(fdt, dtb + i, len); if (redirect) fdt_open_into(fdt, fdt, len + 256); + fdt_list.push_back(fdt); + i += len - 1; int fstab = find_fstab(fdt); if (fstab < 0) @@ -165,17 +168,17 @@ static void dtb_patch(const char *in, const char *out) { fdt_setprop_string(fdt, block, "mnt_point", "/system_root"); } } - fdt_list.push_back(fdt); } } munmap(dtb, dtb_sz); if (modified) { if (!out) out = in; - int fd = xopen(out, O_WRONLY | O_CREAT | O_CLOEXEC); + int fd = xopen(out, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); for (auto fdt : fdt_list) { fdt_pack(fdt); xwrite(fd, fdt, fdt_totalsize(fdt)); + free(fdt); } close(fd); } From e0927cd763ef33a20fa3194a7b4ec63d04cab70e Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 7 Oct 2019 00:38:02 -0400 Subject: [PATCH 20/50] Add support to patch QCDT Old Qualcomn devices have their own special QC table of DTB to store device trees. Since patching fstab is now mandatory on Android 10, and for older devices all early mount devices have to be included into the fstab in DTBs, patching QCDT is crucial for rooting Android 10 on legacy devices. Close #1876 (Thanks for getting me aware of this issue!) --- native/jni/magiskboot/dtb.cpp | 232 ++++++++++++++++++++++++++------- native/jni/magiskboot/format.h | 1 + native/jni/utils/misc.cpp | 17 +++ native/jni/utils/misc.h | 1 + 4 files changed, 206 insertions(+), 45 deletions(-) diff --git a/native/jni/magiskboot/dtb.cpp b/native/jni/magiskboot/dtb.cpp index 0c2be07fd..e43171e1f 100644 --- a/native/jni/magiskboot/dtb.cpp +++ b/native/jni/magiskboot/dtb.cpp @@ -7,6 +7,7 @@ extern "C" { #include #include #include +#include #include "magiskboot.h" #include "format.h" @@ -16,6 +17,59 @@ using namespace std; constexpr int MAX_DEPTH = 32; static bitset depth_set; +struct qcdt_hdr { + char magic[4]; /* "QCDT" */ + uint32_t version; /* QCDT version */ + uint32_t num_dtbs; /* Number of DTBs */ +} __attribute__((packed)); + +struct qctable_v1 { + uint32_t cpu_info[3]; /* Some CPU info */ + uint32_t offset; /* DTB offset in QCDT */ + uint32_t len; /* DTB size */ +} __attribute__((packed)); + +struct qctable_v2 { + uint32_t cpu_info[4]; /* Some CPU info */ + uint32_t offset; /* DTB offset in QCDT */ + uint32_t len; /* DTB size */ +} __attribute__((packed)); + +struct qctable_v3 { + uint32_t cpu_info[8]; /* Some CPU info */ + uint32_t offset; /* DTB offset in QCDT */ + uint32_t len; /* DTB size */ +} __attribute__((packed)); + +struct dtb_blob { + void *fdt; + uint32_t offset; + uint32_t len; +}; + +template +class fdt_map_iter { +public: + typedef decltype(std::declval().fdt) value_type; + typedef value_type* pointer; + typedef value_type& reference; + + explicit fdt_map_iter(Iter j) : i(j) {} + fdt_map_iter& operator++() { ++i; return *this; } + fdt_map_iter operator++(int) { auto tmp = *this; ++(*this); return tmp; } + fdt_map_iter& operator--() { --i; return *this; } + fdt_map_iter operator--(int) { auto tmp = *this; --(*this); return tmp; } + bool operator==(fdt_map_iter j) const { return i == j.i; } + bool operator!=(fdt_map_iter j) const { return !(*this == j); } + reference operator*() { return i->second.fdt; } + pointer operator->() { return &i->second.fdt; } +private: + Iter i; +}; + +template +inline fdt_map_iter make_iter(Iter j) { return fdt_map_iter(j); } + static void pretty_node(int depth) { if (depth == 0) return; @@ -121,59 +175,146 @@ static void dtb_print(const char *file, bool fstab) { } fprintf(stderr, "\n"); munmap(dtb, size); - exit(0); } -static void dtb_patch(const char *in, const char *out) { +template +static bool fdt_patch(Iter first, Iter last) { bool keepverity = check_env("KEEPVERITY"); bool redirect = check_env("TWOSTAGEINIT"); + bool modified = false; - vector fdt_list; + int idx = 0; + for (auto it = first; it != last; ++it) { + ++idx; + auto fdt = *it; + int fstab = find_fstab(fdt); + if (fstab < 0) + continue; + fprintf(stderr, "Found fstab in dtb.%04d\n", idx - 1); + int block; + fdt_for_each_subnode(block, fdt, fstab) { + const char *name = fdt_get_name(fdt, block, nullptr); + fprintf(stderr, "Found entry [%s] in fstab\n", name); + if (!keepverity) { + uint32_t size; + auto value = static_cast( + fdt_getprop(fdt, block, "fsmgr_flags", reinterpret_cast(&size))); + char *pval = patch_verity(value, size); + if (pval) { + modified = true; + fdt_setprop_string(fdt, block, "fsmgr_flags", pval); + } + } + if (redirect && name == "system"sv) { + modified = true; + fprintf(stderr, "Changing mnt_point to /system_root\n"); + fdt_setprop_string(fdt, block, "mnt_point", "/system_root"); + } + } + } + return modified; +} + +template +static int dtb_patch(const qcdt_hdr *hdr, const char *in, const char *out) { + map dtb_map; + auto buf = reinterpret_cast(hdr); + auto tables = reinterpret_cast(hdr + 1); + + // Collect all dtbs + for (int i = 0; i < hdr->num_dtbs; ++i) { + auto it = dtb_map.find(tables[i].offset); + if (it == dtb_map.end()) { + auto fdt = xmalloc(tables[i].len + 256); + memcpy(fdt, buf + tables[i].offset, tables[i].len); + fdt_open_into(fdt, fdt, tables[i].len + 256); + dtb_map[tables[i].offset] = { fdt, tables[i].offset }; + } + } + if (dtb_map.empty()) + return 1; + + // Patch fdt + if (!fdt_patch(make_iter(dtb_map.begin()), make_iter(dtb_map.end()))) + return 1; + + if (out == in) + unlink(in); + int fd = xopen(out, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); + + // Copy headers and tables + xwrite(fd, buf, dtb_map.begin()->first); + + // mmap rw to patch table values retroactively + auto mmap_sz = lseek(fd, 0, SEEK_CUR); + auto addr = (uint8_t *) xmmap(nullptr, mmap_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + // Guess page size using gcd + auto it = dtb_map.begin(); + uint32_t page_size = (it++)->first; + for (; it != dtb_map.end(); ++it) + page_size = binary_gcd(page_size, it->first); + + // Write dtbs + for (auto &val : dtb_map) { + write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), page_size)); + val.second.offset = lseek(fd, 0, SEEK_CUR); + auto fdt = val.second.fdt; + fdt_pack(fdt); + val.second.len = fdt_totalsize(fdt); + xwrite(fd, fdt, val.second.len); + free(fdt); + } + + // Patch tables + auto tables_rw = reinterpret_cast(addr + sizeof(qcdt_hdr)); + for (int i = 0; i < hdr->num_dtbs; ++i) { + auto &blob = dtb_map[tables_rw[i].offset]; + tables_rw[i].offset = blob.offset; + tables_rw[i].len = blob.len; + } + + munmap(addr, mmap_sz); + close(fd); + + return 0; +} + +static int dtb_patch(const char *in, const char *out) { + if (!out) + out = in; size_t dtb_sz ; uint8_t *dtb; fprintf(stderr, "Loading dtbs from [%s]\n", in); mmap_ro(in, dtb, dtb_sz); - bool modified = false; - for (int i = 0; i < dtb_sz; ++i) { - if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) { - int len = fdt_totalsize(dtb + i); - auto fdt = static_cast(xmalloc(redirect ? len + 256 : len)); - memcpy(fdt, dtb + i, len); - if (redirect) - fdt_open_into(fdt, fdt, len + 256); - fdt_list.push_back(fdt); - i += len - 1; + run_finally f([&]{ munmap(dtb, dtb_sz); }); - int fstab = find_fstab(fdt); - if (fstab < 0) - continue; - fprintf(stderr, "Found fstab in dtb.%04d\n", fdt_list.size()); - int block; - fdt_for_each_subnode(block, fdt, fstab) { - const char *name = fdt_get_name(fdt, block, nullptr); - fprintf(stderr, "Found entry [%s] in fstab\n", name); - if (!keepverity) { - uint32_t size; - auto value = static_cast( - fdt_getprop(fdt, block, "fsmgr_flags", reinterpret_cast(&size))); - char *pval = patch_verity(value, size); - if (pval) { - modified = true; - fdt_setprop_string(fdt, block, "fsmgr_flags", pval); - } - } - if (redirect && name == "system"sv) { - modified = true; - fprintf(stderr, "Changing mnt_point to /system_root\n"); - fdt_setprop_string(fdt, block, "mnt_point", "/system_root"); - } + if (memcmp(dtb, QCDT_MAGIC, 4) == 0) { + auto hdr = reinterpret_cast(dtb); + switch (hdr->version) { + case 1: + return dtb_patch(hdr, in, out); + case 2: + return dtb_patch(hdr, in, out); + case 3: + return dtb_patch(hdr, in, out); + default: + return 1; + } + } else { + vector fdt_list; + for (int i = 0; i < dtb_sz; ++i) { + if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) { + int len = fdt_totalsize(dtb + i); + auto fdt = static_cast(xmalloc(len + 256)); + memcpy(fdt, dtb + i, len); + fdt_open_into(fdt, fdt, len + 256); + fdt_list.push_back(fdt); + i += len - 1; } } - } - munmap(dtb, dtb_sz); - if (modified) { - if (!out) - out = in; + if (!fdt_patch(fdt_list.begin(), fdt_list.end())) + return 1; int fd = xopen(out, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); for (auto fdt : fdt_list) { fdt_pack(fdt); @@ -182,7 +323,7 @@ static void dtb_patch(const char *in, const char *out) { } close(fd); } - exit(!modified); + return 0; } int dtb_commands(int argc, char *argv[]) { @@ -192,11 +333,12 @@ int dtb_commands(int argc, char *argv[]) { if (argv[0] == "print"sv) { dtb_print(dtb, argc > 1 && argv[1] == "-f"sv); + return 0; } else if (argv[0] == "patch"sv) { - dtb_patch(dtb, argv[1]); + if (dtb_patch(dtb, argv[1])) + exit(1); + return 0; } else { return 1; } - - return 0; } diff --git a/native/jni/magiskboot/format.h b/native/jni/magiskboot/format.h index a445e6200..1552e5d7c 100644 --- a/native/jni/magiskboot/format.h +++ b/native/jni/magiskboot/format.h @@ -41,6 +41,7 @@ typedef enum { #define DTB_MAGIC "\xd0\x0d\xfe\xed" #define LG_BUMP_MAGIC "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79" #define DHTB_MAGIC "\x44\x48\x54\x42\x01\x00\x00\x00" +#define QCDT_MAGIC "QCDT" #define SEANDROID_MAGIC "SEANDROIDENFORCE" #define TEGRABLOB_MAGIC "-SIGNED-BY-SIGNBLOB-" #define NOOKHD_RL_MAGIC "Red Loader" diff --git a/native/jni/utils/misc.cpp b/native/jni/utils/misc.cpp index 9d680ce5d..b01da66b1 100644 --- a/native/jni/utils/misc.cpp +++ b/native/jni/utils/misc.cpp @@ -181,3 +181,20 @@ int parse_int(const char *s) { } return val; } + +uint32_t binary_gcd(uint32_t u, uint32_t v) { + if (u == 0) return v; + if (v == 0) return u; + auto shift = __builtin_ctz(u | v); + u >>= __builtin_ctz(u); + do { + v >>= __builtin_ctz(v); + if (u > v) { + auto t = v; + v = u; + u = t; + } + v -= u; + } while (v != 0); + return u << shift; +} diff --git a/native/jni/utils/misc.h b/native/jni/utils/misc.h index ad6616431..a55516f30 100644 --- a/native/jni/utils/misc.h +++ b/native/jni/utils/misc.h @@ -14,6 +14,7 @@ char *rtrim(char *str); void init_argv0(int argc, char **argv); void set_nice_name(const char *name); int parse_int(const char *s); +uint32_t binary_gcd(uint32_t u, uint32_t v); #ifdef __cplusplus } From f87ee3fcf9fdeadb1a6bb9d5e9a74e145d214444 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 7 Oct 2019 04:35:02 -0400 Subject: [PATCH 21/50] Refactor boot image unpack/repack code base --- native/jni/magiskboot/bootimg.cpp | 361 ++++++++++++++--------------- native/jni/magiskboot/bootimg.h | 112 +++++---- native/jni/magiskboot/magiskboot.h | 2 +- 3 files changed, 238 insertions(+), 237 deletions(-) diff --git a/native/jni/magiskboot/bootimg.cpp b/native/jni/magiskboot/bootimg.cpp index 4af6c7051..a25185772 100644 --- a/native/jni/magiskboot/bootimg.cpp +++ b/native/jni/magiskboot/bootimg.cpp @@ -60,50 +60,49 @@ static void restore_buf(int fd, const void *buf, size_t size) { boot_img::~boot_img() { munmap(map_addr, map_size); delete hdr; - delete k_hdr; - delete r_hdr; - delete b_hdr; } -#define UNSUPP_RET 1 -#define CHROME_RET 2 -int boot_img::parse_file(const char *image) { +void boot_img::parse_file(const char *image) { mmap_ro(image, map_addr, map_size); fprintf(stderr, "Parsing boot image: [%s]\n", image); - for (uint8_t *head = map_addr; head < map_addr + map_size; ++head) { - switch (check_fmt(head, map_size)) { + for (uint8_t *addr = map_addr; addr < map_addr + map_size; ++addr) { + switch (check_fmt(addr, map_size)) { case CHROMEOS: - // The caller should know it's chromeos, as it needs additional signing + // chromeos require external signing flags |= CHROMEOS_FLAG; + addr += 65535; break; case DHTB: - flags |= DHTB_FLAG; - flags |= SEANDROID_FLAG; + flags |= (DHTB_FLAG | SEANDROID_FLAG); fprintf(stderr, "DHTB_HDR\n"); + addr += sizeof(dhtb_hdr) - 1; break; case BLOB: flags |= BLOB_FLAG; fprintf(stderr, "TEGRA_BLOB\n"); - b_hdr = new blob_hdr(); - memcpy(b_hdr, head, sizeof(blob_hdr)); - head += sizeof(blob_hdr) - 1; + addr += sizeof(blob_hdr) - 1; break; case AOSP: - return parse_image(head); - + parse_image(addr); + return; default: break; } } - exit(UNSUPP_RET); + exit(1); } -#define pos_align() pos = do_align(pos, hdr->page_size()) -int boot_img::parse_image(uint8_t *head) { - auto hp = reinterpret_cast(head); +#define get_block(name) {\ +name = addr + off; \ +off += hdr->name##_size(); \ +off = do_align(off, hdr->page_size()); \ +} + +void boot_img::parse_image(uint8_t *addr) { + auto hp = reinterpret_cast(addr); if (hp->page_size >= 0x02000000) { fprintf(stderr, "PXA_BOOT_HDR\n"); - hdr = new dyn_img_pxa(head); + hdr = new dyn_img_pxa(addr); } else { if (memcmp(hp->cmdline, NOOKHD_RL_MAGIC, 10) == 0 || memcmp(hp->cmdline, NOOKHD_GL_MAGIC, 12) == 0 || @@ -112,53 +111,36 @@ int boot_img::parse_image(uint8_t *head) { memcmp(hp->cmdline, NOOKHD_ER_MAGIC, 30) == 0) { flags |= NOOKHD_FLAG; fprintf(stderr, "NOOKHD_LOADER\n"); - head += NOOKHD_PRE_HEADER_SZ; + addr += NOOKHD_PRE_HEADER_SZ; } else if (memcmp(hp->name, ACCLAIM_MAGIC, 10) == 0) { flags |= ACCLAIM_FLAG; fprintf(stderr, "ACCLAIM_LOADER\n"); - head += ACCLAIM_PRE_HEADER_SZ; + addr += ACCLAIM_PRE_HEADER_SZ; } if (hp->header_version == 1) - hdr = new dyn_img_v1(head); + hdr = new dyn_img_v1(addr); else if (hp->header_version == 2) - hdr = new dyn_img_v2(head); + hdr = new dyn_img_v2(addr); else - hdr = new dyn_img_v0(head); + hdr = new dyn_img_v0(addr); } - - size_t pos = hdr->page_size(); - + img_start = addr; flags |= hdr->id()[SHA_DIGEST_SIZE] ? SHA256_FLAG : 0; print_hdr(); - kernel = head + pos; - pos += hdr->kernel_size(); - pos_align(); + size_t off = hdr->page_size(); - ramdisk = head + pos; - pos += hdr->ramdisk_size(); - pos_align(); + get_block(kernel); + get_block(ramdisk); + get_block(second); + get_block(extra); + get_block(recovery_dtbo); + get_block(dtb); - second = head + pos; - pos += hdr->second_size(); - pos_align(); - - extra = head + pos; - pos += hdr->extra_size(); - pos_align(); - - recov_dtbo = head + pos; - pos += hdr->recovery_dtbo_size(); - pos_align(); - - dtb = head + pos; - pos += hdr->dtb_size(); - pos_align(); - - if (head + pos < map_addr + map_size) { - tail = head + pos; + if (addr + off < map_addr + map_size) { + tail = addr + off; tail_size = map_size - (tail - map_addr); } @@ -169,7 +151,7 @@ int boot_img::parse_image(uint8_t *head) { flags |= LG_BUMP_FLAG; } - find_dtb(); + find_kernel_dtb(); k_fmt = check_fmt(kernel, hdr->kernel_size()); r_fmt = check_fmt(ramdisk, hdr->ramdisk_size()); @@ -178,62 +160,48 @@ int boot_img::parse_image(uint8_t *head) { if (k_fmt == MTK) { fprintf(stderr, "MTK_KERNEL_HDR\n"); flags |= MTK_KERNEL; - k_hdr = new mtk_hdr(); - memcpy(k_hdr, kernel, sizeof(mtk_hdr)); + k_hdr = reinterpret_cast(kernel); fprintf(stderr, "KERNEL [%u]\n", k_hdr->size); fprintf(stderr, "NAME [%s]\n", k_hdr->name); - kernel += 512; - hdr->kernel_size() -= 512; + kernel += sizeof(mtk_hdr); + hdr->kernel_size() -= sizeof(mtk_hdr); k_fmt = check_fmt(kernel, hdr->kernel_size()); } if (r_fmt == MTK) { fprintf(stderr, "MTK_RAMDISK_HDR\n"); flags |= MTK_RAMDISK; - r_hdr = new mtk_hdr(); - memcpy(r_hdr, ramdisk, sizeof(mtk_hdr)); + r_hdr = reinterpret_cast(ramdisk); fprintf(stderr, "RAMDISK [%u]\n", r_hdr->size); fprintf(stderr, "NAME [%s]\n", r_hdr->name); - ramdisk += 512; - hdr->ramdisk_size() -= 512; + ramdisk += sizeof(mtk_hdr); + hdr->ramdisk_size() -= sizeof(mtk_hdr); r_fmt = check_fmt(ramdisk, hdr->ramdisk_size()); } fprintf(stderr, "KERNEL_FMT [%s]\n", fmt2name[k_fmt]); fprintf(stderr, "RAMDISK_FMT [%s]\n", fmt2name[r_fmt]); - - return (flags & CHROMEOS_FLAG) ? CHROME_RET : 0; } -void boot_img::find_dtb() { - for (uint32_t i = 0; i < hdr->kernel_size(); ++i) { +void boot_img::find_kernel_dtb() { + for (int i = 0; i < hdr->kernel_size() - 4; ++i) { auto fdt_hdr = reinterpret_cast(kernel + i); if (fdt32_to_cpu(fdt_hdr->magic) != FDT_MAGIC) continue; // Check that fdt_header.totalsize does not overflow kernel image size uint32_t totalsize = fdt32_to_cpu(fdt_hdr->totalsize); - if (totalsize > hdr->kernel_size() - i) { - fprintf(stderr, "Invalid DTB detection at 0x%x: size (%u) > remaining (%u)\n", - i, totalsize, hdr->kernel_size() - i); + if (totalsize + i > hdr->kernel_size()) continue; - } // Check that fdt_header.off_dt_struct does not overflow kernel image size uint32_t off_dt_struct = fdt32_to_cpu(fdt_hdr->off_dt_struct); - if (off_dt_struct > hdr->kernel_size() - i) { - fprintf(stderr, "Invalid DTB detection at 0x%x: " - "struct offset (%u) > remaining (%u)\n", - i, off_dt_struct, hdr->kernel_size() - i); + if (off_dt_struct + i > hdr->kernel_size()) continue; - } // Check that fdt_node_header.tag of first node is FDT_BEGIN_NODE auto fdt_node_hdr = reinterpret_cast(kernel + i + off_dt_struct); - if (fdt32_to_cpu(fdt_node_hdr->tag) != FDT_BEGIN_NODE) { - fprintf(stderr, "Invalid DTB detection at 0x%x: " - "header tag of first node != FDT_BEGIN_NODE\n", i); + if (fdt32_to_cpu(fdt_node_hdr->tag) != FDT_BEGIN_NODE) continue; - } kernel_dtb = kernel + i; kernel_dt_size = hdr->kernel_size() - i; @@ -282,55 +250,87 @@ void boot_img::print_hdr() { fprintf(stderr, "]\n"); } -int unpack(const char *image, bool hdr) { - boot_img boot {}; - int ret = boot.parse_file(image); - int fd; +static void dump_hdr_file(dyn_img_hdr *hdr) { + FILE *fp = xfopen(HEADER_FILE, "w"); + fprintf(fp, "pagesize=%u\n", hdr->page_size()); + fprintf(fp, "name=%s\n", hdr->name()); + fprintf(fp, "cmdline=%.512s%.1024s\n", hdr->cmdline(), hdr->extra_cmdline()); + uint32_t ver = hdr->os_version(); + if (ver) { + int a, b, c, y, m = 0; + int version, patch_level; + version = ver >> 11; + patch_level = ver & 0x7ff; - if (hdr) { - FILE *fp = xfopen(HEADER_FILE, "w"); - fprintf(fp, "pagesize=%u\n", boot.hdr->page_size()); - fprintf(fp, "name=%s\n", boot.hdr->name()); - fprintf(fp, "cmdline=%.512s%.1024s\n", boot.hdr->cmdline(), boot.hdr->extra_cmdline()); - uint32_t ver = boot.hdr->os_version(); - if (ver) { - int a, b, c, y, m = 0; - int version, patch_level; - version = ver >> 11; - patch_level = ver & 0x7ff; + a = (version >> 14) & 0x7f; + b = (version >> 7) & 0x7f; + c = version & 0x7f; + fprintf(fp, "os_version=%d.%d.%d\n", a, b, c); - a = (version >> 14) & 0x7f; - b = (version >> 7) & 0x7f; - c = version & 0x7f; - fprintf(fp, "os_version=%d.%d.%d\n", a, b, c); - - y = (patch_level >> 4) + 2000; - m = patch_level & 0xf; - fprintf(fp, "os_patch_level=%d-%02d\n", y, m); - } - fclose(fp); + y = (patch_level >> 4) + 2000; + m = patch_level & 0xf; + fprintf(fp, "os_patch_level=%d-%02d\n", y, m); } + fclose(fp); +} + +static void load_hdr_file(dyn_img_hdr *hdr) { + parse_prop_file(HEADER_FILE, [=](string_view key, string_view value) -> bool { + if (key == "page_size") { + hdr->page_size() = parse_int(value); + } else if (key == "name") { + memset(hdr->name(), 0, 16); + memcpy(hdr->name(), value.data(), value.length() > 15 ? 15 : value.length()); + } else if (key == "cmdline") { + memset(hdr->cmdline(), 0, 512); + memset(hdr->extra_cmdline(), 0, 1024); + if (value.length() > 512) { + memcpy(hdr->cmdline(), value.data(), 512); + memcpy(hdr->extra_cmdline(), &value[512], value.length() - 511); + } else { + memcpy(hdr->cmdline(), value.data(), value.length()); + } + } else if (key == "os_version") { + int patch_level = hdr->os_version() & 0x7ff; + int a, b, c; + sscanf(value.data(), "%d.%d.%d", &a, &b, &c); + hdr->os_version() = (((a << 14) | (b << 7) | c) << 11) | patch_level; + } else if (key == "os_patch_level") { + int os_version = hdr->os_version() >> 11; + int y, m; + sscanf(value.data(), "%d-%d", &y, &m); + y -= 2000; + hdr->os_version() = (os_version << 11) | (y << 4) | m; + } + return true; + }); +} + +int unpack(const char *image, bool hdr) { + boot_img boot{}; + boot.parse_file(image); + + if (hdr) + dump_hdr_file(boot.hdr); // Dump kernel if (COMPRESSED(boot.k_fmt)) { - fd = creat(KERNEL_FILE, 0644); + int fd = creat(KERNEL_FILE, 0644); decompress(boot.k_fmt, fd, boot.kernel, boot.hdr->kernel_size()); close(fd); } else { - fprintf(stderr, "Kernel is uncompressed or not a supported compressed type!\n"); dump(boot.kernel, boot.hdr->kernel_size(), KERNEL_FILE); } - // Dump dtb + // Dump kernel_dtb dump(boot.kernel_dtb, boot.kernel_dt_size, KER_DTB_FILE); // Dump ramdisk if (COMPRESSED(boot.r_fmt)) { - fd = creat(RAMDISK_FILE, 0644); + int fd = creat(RAMDISK_FILE, 0644); decompress(boot.r_fmt, fd, boot.ramdisk, boot.hdr->ramdisk_size()); close(fd); } else { - fprintf(stderr, "Ramdisk is uncompressed or not a supported compressed type!\n"); dump(boot.ramdisk, boot.hdr->ramdisk_size(), RAMDISK_FILE); } @@ -341,24 +341,34 @@ int unpack(const char *image, bool hdr) { dump(boot.extra, boot.hdr->extra_size(), EXTRA_FILE); // Dump recovery_dtbo - dump(boot.recov_dtbo, boot.hdr->recovery_dtbo_size(), RECV_DTBO_FILE); + dump(boot.recovery_dtbo, boot.hdr->recovery_dtbo_size(), RECV_DTBO_FILE); // Dump dtb dump(boot.dtb, boot.hdr->dtb_size(), DTB_FILE); - return ret; + + return (boot.flags & CHROMEOS_FLAG) ? 2 : 0; } #define file_align() \ -write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR) - header_off, boot.hdr->page_size())) +write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR) - off.header, boot.hdr->page_size())) -void repack(const char* orig_image, const char* out_image, bool force_nocomp) { - boot_img boot {}; +void repack(const char* orig_image, const char* out_image, bool nocomp) { + boot_img boot{}; - off_t header_off, kernel_off, ramdisk_off, second_off, extra_off, dtb_off; + struct { + uint32_t header; + uint32_t kernel; + uint32_t ramdisk; + uint32_t second; + uint32_t extra; + uint32_t dtb; + } off; // Parse original image boot.parse_file(orig_image); + fprintf(stderr, "Repack to boot image: [%s]\n", out_image); + // Reset sizes boot.hdr->kernel_size() = 0; boot.hdr->ramdisk_size() = 0; @@ -366,65 +376,36 @@ void repack(const char* orig_image, const char* out_image, bool force_nocomp) { boot.hdr->dtb_size() = 0; boot.kernel_dt_size = 0; - fprintf(stderr, "Repack to boot image: [%s]\n", out_image); + if (access(HEADER_FILE, R_OK) == 0) + load_hdr_file(boot.hdr); + + /***************** + * Writing blocks + *****************/ // Create new image int fd = creat(out_image, 0644); if (boot.flags & DHTB_FLAG) { // Skip DHTB header - write_zero(fd, 512); + write_zero(fd, sizeof(dhtb_hdr)); } else if (boot.flags & BLOB_FLAG) { - // Skip blob header - write_zero(fd, sizeof(blob_hdr)); + restore_buf(fd, boot.map_addr, sizeof(blob_hdr)); } else if (boot.flags & NOOKHD_FLAG) { restore_buf(fd, boot.map_addr, NOOKHD_PRE_HEADER_SZ); } else if (boot.flags & ACCLAIM_FLAG) { restore_buf(fd, boot.map_addr, ACCLAIM_PRE_HEADER_SZ); } - // header - if (access(HEADER_FILE, R_OK) == 0) { - parse_prop_file(HEADER_FILE, [&](string_view key, string_view value) -> bool { - if (key == "page_size") { - boot.hdr->page_size() = parse_int(value); - } else if (key == "name") { - memset(boot.hdr->name(), 0, 16); - memcpy(boot.hdr->name(), value.data(), value.length() > 15 ? 15 : value.length()); - } else if (key == "cmdline") { - memset(boot.hdr->cmdline(), 0, 512); - memset(boot.hdr->extra_cmdline(), 0, 1024); - if (value.length() > 512) { - memcpy(boot.hdr->cmdline(), value.data(), 512); - memcpy(boot.hdr->extra_cmdline(), &value[512], value.length() - 511); - } else { - memcpy(boot.hdr->cmdline(), value.data(), value.length()); - } - } else if (key == "os_version") { - int patch_level = boot.hdr->os_version() & 0x7ff; - int a, b, c; - sscanf(value.data(), "%d.%d.%d", &a, &b, &c); - boot.hdr->os_version() = (((a << 14) | (b << 7) | c) << 11) | patch_level; - } else if (key == "os_patch_level") { - int os_version = boot.hdr->os_version() >> 11; - int y, m; - sscanf(value.data(), "%d-%d", &y, &m); - y -= 2000; - boot.hdr->os_version() = (os_version << 11) | (y << 4) | m; - } - return true; - }); - } - - // Skip a page for header - header_off = lseek(fd, 0, SEEK_CUR); - write_zero(fd, boot.hdr->page_size()); + // Copy a page for header + off.header = lseek(fd, 0, SEEK_CUR); + restore_buf(fd, boot.img_start, boot.hdr->page_size()); // kernel - kernel_off = lseek(fd, 0, SEEK_CUR); + off.kernel = lseek(fd, 0, SEEK_CUR); if (boot.flags & MTK_KERNEL) { - // Skip MTK header - write_zero(fd, 512); + // Copy MTK headers + restore_buf(fd, boot.k_hdr, sizeof(mtk_hdr)); } if (access(KERNEL_FILE, R_OK) == 0) { size_t raw_size; @@ -433,7 +414,7 @@ void repack(const char* orig_image, const char* out_image, bool force_nocomp) { if (!COMPRESSED_ANY(check_fmt(raw_buf, raw_size)) && COMPRESSED(boot.k_fmt)) { boot.hdr->kernel_size() = compress(boot.k_fmt, fd, raw_buf, raw_size); } else { - boot.hdr->kernel_size() = write(fd, raw_buf, raw_size); + boot.hdr->kernel_size() = xwrite(fd, raw_buf, raw_size); } munmap(raw_buf, raw_size); } @@ -444,33 +425,33 @@ void repack(const char* orig_image, const char* out_image, bool force_nocomp) { file_align(); // ramdisk - ramdisk_off = lseek(fd, 0, SEEK_CUR); + off.ramdisk = lseek(fd, 0, SEEK_CUR); if (boot.flags & MTK_RAMDISK) { - // Skip MTK header - write_zero(fd, 512); + // Copy MTK headers + restore_buf(fd, boot.r_hdr, sizeof(mtk_hdr)); } if (access(RAMDISK_FILE, R_OK) == 0) { size_t raw_size; void *raw_buf; mmap_ro(RAMDISK_FILE, raw_buf, raw_size); - if (!COMPRESSED_ANY(check_fmt(raw_buf, raw_size)) && COMPRESSED(boot.r_fmt) && !force_nocomp) { + if (!COMPRESSED_ANY(check_fmt(raw_buf, raw_size)) && COMPRESSED(boot.r_fmt) && !nocomp) { boot.hdr->ramdisk_size() = compress(boot.r_fmt, fd, raw_buf, raw_size); } else { - boot.hdr->ramdisk_size() = write(fd, raw_buf, raw_size); + boot.hdr->ramdisk_size() = xwrite(fd, raw_buf, raw_size); } munmap(raw_buf, raw_size); file_align(); } // second - second_off = lseek(fd, 0, SEEK_CUR); + off.second = lseek(fd, 0, SEEK_CUR); if (access(SECOND_FILE, R_OK) == 0) { boot.hdr->second_size() = restore(SECOND_FILE, fd); file_align(); } // extra - extra_off = lseek(fd, 0, SEEK_CUR); + off.extra = lseek(fd, 0, SEEK_CUR); if (access(EXTRA_FILE, R_OK) == 0) { boot.hdr->extra_size() = restore(EXTRA_FILE, fd); file_align(); @@ -484,7 +465,7 @@ void repack(const char* orig_image, const char* out_image, bool force_nocomp) { } // dtb - dtb_off = lseek(fd, 0, SEEK_CUR); + off.dtb = lseek(fd, 0, SEEK_CUR); if (access(DTB_FILE, R_OK) == 0) { boot.hdr->dtb_size() = restore(DTB_FILE, fd); file_align(); @@ -500,37 +481,41 @@ void repack(const char* orig_image, const char* out_image, bool force_nocomp) { close(fd); + /********************* + * Patching the image + *********************/ + // Map output image as rw munmap(boot.map_addr, boot.map_size); mmap_rw(out_image, boot.map_addr, boot.map_size); // MTK headers if (boot.flags & MTK_KERNEL) { - boot.k_hdr->size = boot.hdr->kernel_size(); - boot.hdr->kernel_size() += 512; - memcpy(boot.map_addr + kernel_off, boot.k_hdr, sizeof(mtk_hdr)); + auto hdr = reinterpret_cast(boot.map_addr + off.kernel); + hdr->size = boot.hdr->kernel_size(); + boot.hdr->kernel_size() += sizeof(*hdr); } if (boot.flags & MTK_RAMDISK) { - boot.r_hdr->size = boot.hdr->ramdisk_size(); - boot.hdr->ramdisk_size() += 512; - memcpy(boot.map_addr + ramdisk_off, boot.r_hdr, sizeof(mtk_hdr)); + auto hdr = reinterpret_cast(boot.map_addr + off.ramdisk); + hdr->size = boot.hdr->ramdisk_size(); + boot.hdr->ramdisk_size() += sizeof(*hdr); } // Update checksum HASH_CTX ctx; (boot.flags & SHA256_FLAG) ? SHA256_init(&ctx) : SHA_init(&ctx); uint32_t size = boot.hdr->kernel_size(); - HASH_update(&ctx, boot.map_addr + kernel_off, size); + HASH_update(&ctx, boot.map_addr + off.kernel, size); HASH_update(&ctx, &size, sizeof(size)); size = boot.hdr->ramdisk_size(); - HASH_update(&ctx, boot.map_addr + ramdisk_off, size); + HASH_update(&ctx, boot.map_addr + off.ramdisk, size); HASH_update(&ctx, &size, sizeof(size)); size = boot.hdr->second_size(); - HASH_update(&ctx, boot.map_addr + second_off, size); + HASH_update(&ctx, boot.map_addr + off.second, size); HASH_update(&ctx, &size, sizeof(size)); size = boot.hdr->extra_size(); if (size) { - HASH_update(&ctx, boot.map_addr + extra_off, size); + HASH_update(&ctx, boot.map_addr + off.extra, size); HASH_update(&ctx, &size, sizeof(size)); } if (boot.hdr->header_version()) { @@ -539,9 +524,11 @@ void repack(const char* orig_image, const char* out_image, bool force_nocomp) { HASH_update(&ctx, &size, sizeof(size)); size = boot.hdr->dtb_size(); if (size) { - HASH_update(&ctx, boot.map_addr + dtb_off, size); + HASH_update(&ctx, boot.map_addr + off.dtb, size); HASH_update(&ctx, &size, sizeof(size)); } + + boot.hdr->header_size() = boot.hdr->hdr_size(); } memset(boot.hdr->id(), 0, 32); memcpy(boot.hdr->id(), HASH_final(&ctx), @@ -550,22 +537,18 @@ void repack(const char* orig_image, const char* out_image, bool force_nocomp) { // Print new image info boot.print_hdr(); - // Try to fix the header - if (boot.hdr->header_version() && boot.hdr->header_size() == 0) - boot.hdr->header_size() = sizeof(boot_img_hdr); - // Main header - memcpy(boot.map_addr + header_off, **boot.hdr, boot.hdr->hdr_size()); + memcpy(boot.map_addr + off.header, **boot.hdr, boot.hdr->hdr_size()); if (boot.flags & DHTB_FLAG) { // DHTB header - dhtb_hdr *hdr = reinterpret_cast(boot.map_addr); + auto hdr = reinterpret_cast(boot.map_addr); memcpy(hdr, DHTB_MAGIC, 8); - hdr->size = boot.map_size - 512; - SHA256_hash(boot.map_addr + 512, hdr->size, hdr->checksum); + hdr->size = boot.map_size - sizeof(dhtb_hdr); + SHA256_hash(boot.map_addr + sizeof(dhtb_hdr), hdr->size, hdr->checksum); } else if (boot.flags & BLOB_FLAG) { - // Blob headers - boot.b_hdr->size = boot.map_size - sizeof(blob_hdr); - memcpy(boot.map_addr, boot.b_hdr, sizeof(blob_hdr)); + // Blob header + auto hdr = reinterpret_cast(boot.map_addr); + hdr->size = boot.map_size - sizeof(blob_hdr); } } diff --git a/native/jni/magiskboot/bootimg.h b/native/jni/magiskboot/bootimg.h index 75e0e6a09..ee34e6cd1 100644 --- a/native/jni/magiskboot/bootimg.h +++ b/native/jni/magiskboot/bootimg.h @@ -4,6 +4,47 @@ #include #include "format.h" +/**************** + * Other Headers + ****************/ + +struct mtk_hdr { + uint32_t magic; /* MTK magic */ + uint32_t size; /* Size of the content */ + char name[32]; /* The type of the header */ + + char padding[472]; /* Padding to 512 bytes */ +} __attribute__((packed)); + +struct dhtb_hdr { + char magic[8]; /* DHTB magic */ + uint8_t checksum[40]; /* Payload SHA256, whole image + SEANDROIDENFORCE + 0xFFFFFFFF */ + uint32_t size; /* Payload size, whole image + SEANDROIDENFORCE + 0xFFFFFFFF */ + + char padding[460]; /* Padding to 512 bytes */ +} __attribute__((packed)); + +struct blob_hdr { + char secure_magic[20]; /* "-SIGNED-BY-SIGNBLOB-" */ + uint32_t datalen; /* 0x00000000 */ + uint32_t signature; /* 0x00000000 */ + char magic[16]; /* "MSM-RADIO-UPDATE" */ + uint32_t hdr_version; /* 0x00010000 */ + uint32_t hdr_size; /* Size of header */ + uint32_t part_offset; /* Same as size */ + uint32_t num_parts; /* Number of partitions */ + uint32_t unknown[7]; /* All 0x00000000 */ + char name[4]; /* Name of partition */ + uint32_t offset; /* offset in blob where this partition starts */ + uint32_t size; /* Size of data */ + uint32_t version; /* 0x00000001 */ +} __attribute__((packed)); + + +/********************* + * Boot Image Headers + *********************/ + struct boot_img_hdr_base { char magic[8]; @@ -57,12 +98,12 @@ struct boot_img_hdr_v2 : public boot_img_hdr_v1 { } __attribute__((packed)); // Default to hdr v2 -typedef boot_img_hdr_v2 boot_img_hdr; +using boot_img_hdr = boot_img_hdr_v2; // Special Samsung header struct boot_img_hdr_pxa : public boot_img_hdr_base { uint32_t extra_size; /* extra blob size in bytes */ - uint32_t unknown; /* unknown value */ + uint32_t unknown; uint32_t tags_addr; /* physical addr for kernel tags */ uint32_t page_size; /* flash page size we assume */ @@ -111,34 +152,6 @@ struct boot_img_hdr_pxa : public boot_img_hdr_base { ** else: jump to kernel_addr */ -struct mtk_hdr { - uint32_t magic; /* MTK magic */ - uint32_t size; /* Size of the content */ - char name[32]; /* The type of the header */ -} __attribute__((packed)); - -struct dhtb_hdr { - char magic[8]; /* DHTB magic */ - uint8_t checksum[40]; /* Payload SHA256, whole image + SEANDROIDENFORCE + 0xFFFFFFFF */ - uint32_t size; /* Payload size, whole image + SEANDROIDENFORCE + 0xFFFFFFFF */ -} __attribute__((packed)); - -struct blob_hdr { - char secure_magic[20]; /* "-SIGNED-BY-SIGNBLOB-" */ - uint32_t datalen; /* 0x00000000 */ - uint32_t signature; /* 0x00000000 */ - char magic[16]; /* "MSM-RADIO-UPDATE" */ - uint32_t hdr_version; /* 0x00010000 */ - uint32_t hdr_size; /* Size of header */ - uint32_t part_offset; /* Same as size */ - uint32_t num_parts; /* Number of partitions */ - uint32_t unknown[7]; /* All 0x00000000 */ - char name[4]; /* Name of partition */ - uint32_t offset; /* offset in blob where this partition starts */ - uint32_t size; /* Size of data */ - uint32_t version; /* 0x00000001 */ -} __attribute__((packed)); - #define drct_var(name) \ auto &name() { return img_hdr->name; } #define decl_var(name, len) \ @@ -269,16 +282,16 @@ struct dyn_img_v2 : public dyn_img_v1 { #undef impl_val // Flags -#define MTK_KERNEL 1 << 1 -#define MTK_RAMDISK 1 << 2 -#define CHROMEOS_FLAG 1 << 3 -#define DHTB_FLAG 1 << 4 -#define SEANDROID_FLAG 1 << 5 -#define LG_BUMP_FLAG 1 << 6 -#define SHA256_FLAG 1 << 7 -#define BLOB_FLAG 1 << 8 -#define NOOKHD_FLAG 1 << 9 -#define ACCLAIM_FLAG 1 << 10 +#define MTK_KERNEL (1 << 0) +#define MTK_RAMDISK (1 << 1) +#define CHROMEOS_FLAG (1 << 2) +#define DHTB_FLAG (1 << 3) +#define SEANDROID_FLAG (1 << 4) +#define LG_BUMP_FLAG (1 << 5) +#define SHA256_FLAG (1 << 6) +#define BLOB_FLAG (1 << 7) +#define NOOKHD_FLAG (1 << 8) +#define ACCLAIM_FLAG (1 << 9) struct boot_img { // Memory map of the whole image @@ -287,9 +300,6 @@ struct boot_img { // Headers dyn_img_hdr *hdr; /* Android image header */ - mtk_hdr *k_hdr; /* MTK kernel header */ - mtk_hdr *r_hdr; /* MTK ramdisk header */ - blob_hdr *b_hdr; /* Tegra blob header */ // Flags to indicate the state of current boot image uint16_t flags; @@ -298,6 +308,13 @@ struct boot_img { format_t k_fmt; format_t r_fmt; + /*************************************************** + * Following pointers points within the mmap region + ***************************************************/ + + mtk_hdr *k_hdr; /* MTK kernel header */ + mtk_hdr *r_hdr; /* MTK ramdisk header */ + // Pointer to dtb that is appended after kernel uint8_t *kernel_dtb; uint32_t kernel_dt_size; @@ -307,17 +324,18 @@ struct boot_img { size_t tail_size; // Pointers to blocks defined in header + uint8_t *img_start; uint8_t *kernel; uint8_t *ramdisk; uint8_t *second; uint8_t *extra; - uint8_t *recov_dtbo; + uint8_t *recovery_dtbo; uint8_t *dtb; ~boot_img(); - int parse_file(const char *); - int parse_image(uint8_t *); - void find_dtb(); + void parse_file(const char *); + void parse_image(uint8_t *addr); + void find_kernel_dtb(); void print_hdr(); }; diff --git a/native/jni/magiskboot/magiskboot.h b/native/jni/magiskboot/magiskboot.h index 1cbbd82ea..cfbe0e151 100644 --- a/native/jni/magiskboot/magiskboot.h +++ b/native/jni/magiskboot/magiskboot.h @@ -13,7 +13,7 @@ #define NEW_BOOT "new-boot.img" int unpack(const char *image, bool hdr = false); -void repack(const char* orig_image, const char* out_image, bool force_nocomp = false); +void repack(const char* orig_image, const char* out_image, bool nocomp = false); int hexpatch(const char *image, const char *from, const char *to); int cpio_commands(int argc, char *argv[]); int dtb_commands(int argc, char *argv[]); From b45d922463272e8b136cbcd9d7613cc6f129d7be Mon Sep 17 00:00:00 2001 From: Alessandro Astone Date: Mon, 7 Oct 2019 23:44:03 +0200 Subject: [PATCH 22/50] qcdt: include padding in the table length fields --- native/jni/magiskboot/dtb.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/native/jni/magiskboot/dtb.cpp b/native/jni/magiskboot/dtb.cpp index e43171e1f..fcf681b2b 100644 --- a/native/jni/magiskboot/dtb.cpp +++ b/native/jni/magiskboot/dtb.cpp @@ -261,8 +261,9 @@ static int dtb_patch(const qcdt_hdr *hdr, const char *in, const char *out) { val.second.offset = lseek(fd, 0, SEEK_CUR); auto fdt = val.second.fdt; fdt_pack(fdt); - val.second.len = fdt_totalsize(fdt); - xwrite(fd, fdt, val.second.len); + int size = fdt_totalsize(fdt); + xwrite(fd, fdt, size); + val.second.len = do_align(size, page_size); free(fdt); } From dfc65b95f7006b48be4cb8e91da425afb002764f Mon Sep 17 00:00:00 2001 From: Alessandro Astone Date: Mon, 7 Oct 2019 23:45:16 +0200 Subject: [PATCH 23/50] qcdt: pad the last dtb too --- native/jni/magiskboot/dtb.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/native/jni/magiskboot/dtb.cpp b/native/jni/magiskboot/dtb.cpp index fcf681b2b..aa7884264 100644 --- a/native/jni/magiskboot/dtb.cpp +++ b/native/jni/magiskboot/dtb.cpp @@ -266,6 +266,7 @@ static int dtb_patch(const qcdt_hdr *hdr, const char *in, const char *out) { val.second.len = do_align(size, page_size); free(fdt); } + write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), page_size)); // Patch tables auto tables_rw = reinterpret_cast
(addr + sizeof(qcdt_hdr)); From d6c2c821a479496a0f5cfe7c143b3026d1a00a8a Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 7 Oct 2019 22:57:01 -0400 Subject: [PATCH 24/50] Minor improvements in QCDT logic --- native/jni/magiskboot/dtb.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/native/jni/magiskboot/dtb.cpp b/native/jni/magiskboot/dtb.cpp index aa7884264..b1a009538 100644 --- a/native/jni/magiskboot/dtb.cpp +++ b/native/jni/magiskboot/dtb.cpp @@ -223,11 +223,12 @@ static int dtb_patch(const qcdt_hdr *hdr, const char *in, const char *out) { // Collect all dtbs for (int i = 0; i < hdr->num_dtbs; ++i) { - auto it = dtb_map.find(tables[i].offset); - if (it == dtb_map.end()) { - auto fdt = xmalloc(tables[i].len + 256); - memcpy(fdt, buf + tables[i].offset, tables[i].len); - fdt_open_into(fdt, fdt, tables[i].len + 256); + if (dtb_map.find(tables[i].offset) == dtb_map.end()) { + auto blob = buf + tables[i].offset; + int size = fdt_totalsize(blob); + auto fdt = xmalloc(size + 256); + memcpy(fdt, blob, size); + fdt_open_into(fdt, fdt, size + 256); dtb_map[tables[i].offset] = { fdt, tables[i].offset }; } } @@ -257,16 +258,15 @@ static int dtb_patch(const qcdt_hdr *hdr, const char *in, const char *out) { // Write dtbs for (auto &val : dtb_map) { - write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), page_size)); val.second.offset = lseek(fd, 0, SEEK_CUR); auto fdt = val.second.fdt; fdt_pack(fdt); int size = fdt_totalsize(fdt); xwrite(fd, fdt, size); val.second.len = do_align(size, page_size); + write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), page_size)); free(fdt); } - write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), page_size)); // Patch tables auto tables_rw = reinterpret_cast
(addr + sizeof(qcdt_hdr)); From 751642b39acfb2bd8d5e2a5309f58aab35f453d1 Mon Sep 17 00:00:00 2001 From: Viktor De Pasquale Date: Mon, 7 Oct 2019 12:04:41 +0200 Subject: [PATCH 25/50] Fixed back button not working on flash screen --- .../java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt index 79e573dca..a1f2fe4d0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt @@ -11,6 +11,8 @@ import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.databinding.ActivityFlashBinding +import com.topjohnwu.magisk.model.events.BackPressEvent +import com.topjohnwu.magisk.model.events.ViewEvent import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import java.io.File @@ -37,6 +39,13 @@ open class FlashActivity : BaseActivity() super.onBackPressed() } + override fun onEventDispatched(event: ViewEvent) { + super.onEventDispatched(event) + when (event) { + is BackPressEvent -> onBackPressed() + } + } + companion object { private fun intent(context: Context) = Intent(context, ClassMap[FlashActivity::class.java]) From 2b3cc2896654ea3dd0d1d0b2a2bf8ad87abad206 Mon Sep 17 00:00:00 2001 From: Viktor De Pasquale Date: Mon, 7 Oct 2019 16:12:42 +0200 Subject: [PATCH 26/50] Fixed snackbar not showing up for dumping files --- .../main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt index a1f2fe4d0..3b2d3c293 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt @@ -11,7 +11,9 @@ import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.R import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.databinding.ActivityFlashBinding +import com.topjohnwu.magisk.extensions.snackbar import com.topjohnwu.magisk.model.events.BackPressEvent +import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.model.events.ViewEvent import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf @@ -42,6 +44,7 @@ open class FlashActivity : BaseActivity() override fun onEventDispatched(event: ViewEvent) { super.onEventDispatched(event) when (event) { + is SnackbarEvent -> snackbar(snackbarView, event.message(this), event.length, event.f) is BackPressEvent -> onBackPressed() } } From 3ea28b0afb7f3d9bbec8318d2263e16ffff53b66 Mon Sep 17 00:00:00 2001 From: Viktor De Pasquale Date: Mon, 7 Oct 2019 17:26:10 +0200 Subject: [PATCH 27/50] Fixed permission event not being executed --- .../java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt index 3b2d3c293..2f46401a4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt @@ -13,6 +13,7 @@ import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.databinding.ActivityFlashBinding import com.topjohnwu.magisk.extensions.snackbar import com.topjohnwu.magisk.model.events.BackPressEvent +import com.topjohnwu.magisk.model.events.PermissionEvent import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.model.events.ViewEvent import org.koin.androidx.viewmodel.ext.android.viewModel @@ -46,6 +47,13 @@ open class FlashActivity : BaseActivity() when (event) { is SnackbarEvent -> snackbar(snackbarView, event.message(this), event.length, event.f) is BackPressEvent -> onBackPressed() + is PermissionEvent -> withPermissions(*event.permissions.toTypedArray()) { + onSuccess { event.callback.onNext(true) } + onFailure { + event.callback.onNext(false) + event.callback.onError(SecurityException("User refused permissions")) + } + } } } From 479972e3ae989f1e9fe00f3493c3186e76ac0d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mevl=C3=BCt=20TOP=C3=87U?= Date: Tue, 24 Sep 2019 16:12:00 +0300 Subject: [PATCH 28/50] Update Turkish language Hi Merge please Thank's --- app/src/main/res/values-tr/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 04dbb2e20..701a610a0 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -24,6 +24,7 @@ Gelişmiş Ayarlar Şifrelemeyi zorlamayı sürdür AVB 2.0/dm-verity\'i koru + Kurtarma Modu Yüklü: %1$s Güncel: %1$s Kaldır From f8b4190a11fc5dd3a5c7162d72a27b5bf174ef4a Mon Sep 17 00:00:00 2001 From: Gaurav Date: Mon, 7 Oct 2019 17:18:25 +0530 Subject: [PATCH 29/50] Fix Typos --- docs/install.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/install.md b/docs/install.md index 4a6d5f265..b166e1609 100644 --- a/docs/install.md +++ b/docs/install.md @@ -2,13 +2,13 @@ If you already have Magisk installed, it is **strongly recommended to upgrade directly via Magisk Manager**. The following tutorial is for first time users. ## Getting Started -- If you are using a Huawei device running **EMUI 8 and higher**, please check [its own section](#huawei). -- If you are using a Samsung device that is **launched with Android 9.0** (new devices in 2019), please check [its own section](#samsung-system-as-root). +- If you are using a Huawei device running **EMUI 8 and higher**, please check [its section](#huawei). +- If you are using a Samsung device that is **launched with Android 9.0** (new devices in 2019), please check [its section](#samsung-system-as-root). Otherwise, follow the instructions in [Knowing Your Device](#knowing-your-device), and choose the right steps - If your device is **NOT** A/B, but **IS** using system-as-root, then you will have to install Magisk to the recovery partition of your device. Follow the instructions in [Boot Image Patching](#boot-image-patching), but instead of using your boot image, use your recovery image. **Read through the [Magisk in Recovery](#magisk-in-recovery) section!** -- Otherwise, you can either follow the instructions in [Custom Recovery](#custom-recovery) (if your device have custom recovery available) or [Boot Image Patching](#boot-image-patching). +- Otherwise, you can either follow the instructions in [Custom Recovery](#custom-recovery) (if your device has custom recovery available) or [Boot Image Patching](#boot-image-patching). Other notes: @@ -33,7 +33,7 @@ If the result is `true`, then your device is using system-as-root. (P.S. If you are interested more regarding system-as-root, please check [this Twitter thread](https://twitter.com/topjohnwu/status/1174392824625676288)) ## Custom Recovery -If your device have custom recovery support, the easiest way is to install it through custom recoveries, such as TWRP. +If your device has custom recovery support, the easiest way is to install it through custom recoveries, such as TWRP. - Download the Magisk installer zip - Reboot to custom recovery @@ -41,9 +41,9 @@ If your device have custom recovery support, the easiest way is to install it th - Check whether Magisk Manager is installed. If for some reason it isn't installed automatically, manually install the APK ## Boot Image Patching -You would want choose this method if either your device does not have custom recoveries, your device is A/B and you don't want to mix recovery and boot images, or your device is using system-as-root without A/B partitions. +You would want to choose this method if either your device does not have custom recoveries, your device is A/B and you don't want to mix recovery and boot images, or your device is using system-as-root without A/B partitions. -In order to use this method, you are required to obtain a copy of the stock boot/recovery image, which can be found by extracting OEM provided factory images or extracting from OTA update zips. If you are unable to obtain one yourself, you might be able to find it somewhere on the internet. The following instructions will guide you through the process after you have the copy of boot/recovery image. +To use this method, you are required to obtain a copy of the stock boot/recovery image, which can be found by extracting OEM provided factory images or extracting from OTA update zips. If you are unable to obtain one yourself, you might be able to find it somewhere on the internet. The following instructions will guide you through the process after you have the copy of boot/recovery image. - Copy the boot/recovery image to your device - Download and install the latest Magisk Manager @@ -57,7 +57,7 @@ In order to use this method, you are required to obtain a copy of the stock boot `fastboot flash recovery /path/to/magisk_patched.img` if you are patching a recovery image ## Magisk in Recovery -Due to the fact that some devices no longer uses ramdisk in boot images, Magisk has no choice but to be installed in the recovery partition. For these devices, you will have to **boot to recovery every time** if you want Magisk. Since both Magisk and recovery lives in the same partition, what you actually end up getting when you choose to boot to recovery will be determined by **how long you press volume up**. +Since some devices no longer use ramdisk in boot images, Magisk has no choice but to be installed in the recovery partition. For these devices, you will have to **boot to recovery every time** if you want Magisk. Since both Magisk and recovery lives in the same partition, what you actually end up getting when you choose to boot to recovery will be determined by **how long you press volume up**. Each OEM and device has its own key combo to boot into recovery. For example on Samsung S10 it is **(Power + Bixby + Volume Up)**, and for Huawei it is **(Power + Volume Up)**. As soon as you press the combo and the device vibrates with a splash screen, the bootloader has already chosen which mode it is booting, either it be `boot`, `recovery`, or some OEM specific modes like `download`, `fastboot`, or `erecovery`. After the splash screen, release all buttons to boot into Magisk, since by default `recovery` mode will boot to the system with Magisk enabled. If you decide to boot to actual recovery, continue to press volume up until you see the recovery screen. @@ -105,7 +105,7 @@ Press *Power + Volume Down* to exit download mode. As soon as the screen turns o 10. Use volume buttons to navigate through the stock recovery menu, and the power button to select an option. Choose *Wipe data/factory reset* to wipe the data of the device. 11. This time, we can finally boot to the system with Magisk. Select *Reboot system now*, and immediately press the combo key to recovery. After seeing the bootloader warning screen, release all buttons so it can boot to the system. 12. The device will automatically reboot for the first time it boots. This is completely normal and done by design. -13. After the device is booted up, do the usual initial setup. **The following steps will need internet connection.** +13. After the device is booted up, do the usual initial setup. **The following steps will need an internet connection.** 14. You shall see Magisk Manager in your app drawer; if not, manually install the APK you downloaded in step 3 and continue to the next step. The app would be a stub and it shall automatically upgrade to the full Magisk Manager when you open it. 15. Magisk Manager will ask to do additional setups. Let it do its job and the app will automatically reboot your device. 16. Voila! Enjoy Magisk :) @@ -116,17 +116,17 @@ Press *Power + Volume Down* to exit download mode. As soon as the screen turns o - `boot`: remove the signature of the image to prevent soft bricks - `recovery`: this is where Magisk is actually installed - **Never, ever** try to restore either of the 3 images mentioned back to stock! You can easily brick your device by doing so, and the only way out is to do full Odin restore following with factory reset. Just don't do it. -- If you want to upgrade your device, **never** flash the stock **AP** tar file with reasons mentioned above. **Always** pre-patch the firmware before flashing in Odin. +- If you want to upgrade your device, **never** flash the stock **AP** tar file with the reasons mentioned above. **Always** pre-patch the firmware before flashing in Odin. - If you don't need to patch the full firmware, you can manually create a tar file with **at least** `vbmeta.img`, `boot.img`, and `recovery.img` to let Magisk Manager patch your images in the proper way. ## Huawei -Huawei devices using Kirin processors have a different partitioning method from most common devices. Magisk is usually installed to the `boot` partition of the device, however Huawei devices does not have this partition. Depending on what EMUI version your device is running, the instructions will be slightly different. +Huawei devices using Kirin processors have a different partitioning method from most common devices. Magisk is usually installed to the `boot` partition of the device, however Huawei devices do not have this partition. Depending on what EMUI version your device is running, the instructions will be slightly different. ### Obtain Stock Images Huawei does not release official factory images, however most firmware zips can be downloaded from the [Huawei Firmware Database](http://pro-teammt.ru/firmware-database/). To extract images from `UPDATE.APP` in the zip, you have to use [Huawei Update Extractor](https://forum.xda-developers.com/showthread.php?t=2433454) (Windows only!) ### EMUI 8 -For EMUI 8 devices, your device have a partition named `ramdisk`, which is where Magisk is going to be installed. +For EMUI 8 devices, your device has a partition named `ramdisk`, which is where Magisk is going to be installed. - If you plan to use custom recoveries, simply follow the instructions for custom recovery and you're all set. - If you plan not to use custom recoveries, you will have to extract `RAMDISK.img` from your firmware. Follow the instructions for boot image patching above, but use the `RAMDISK.img` file instead of a boot image. From dbc8bed2345749fd9d01aac826c92505e0b7aeb9 Mon Sep 17 00:00:00 2001 From: Madis Date: Mon, 7 Oct 2019 20:37:36 +0300 Subject: [PATCH 30/50] Estonian update --- app/src/main/res/values-et/strings.xml | 33 ++++++++++++++++------- shared/src/main/res/values-et/strings.xml | 2 -- stub/src/main/res/values-et/strings.xml | 5 ++-- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index fd712283e..46006d63b 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -8,10 +8,10 @@ Seaded Installi Mittetoetatud Magisk\'i versioon - See Magisk Manager\'i versioon ei toeta Magisk\'i v18.0-st vanemat versiooni.\n\nSa võid kas Magisk\'i käsitsi täiendada või alandad rakenduse vanemale versioonile. + See Magisk Manager\'i versioon ei toeta Magisk\'ist vanemat versiooni kui v18.0.\n\nSa võid kas Magisk\'i käsitsi täiendada või alandad rakenduse vanemale versioonile. - Magisk pole installitud. + Magisk pole installitud Kontrollin uuendusi… Sobimatu uuenduste kanal Koputa SafetyNet\'i kontrolliks @@ -24,6 +24,7 @@ Täpsemad seaded Säilita sunnitud krüpteering Säilita AVB 2.0/dm-verity + Taastusrežiim Installitud: %1$s Viimatine: %1$s Eemalda @@ -44,6 +45,7 @@ Taaskäivita taastusesse Taaskäivita käivitushaldurisse Taaskäivita allalaadimisrežiimi + Taaskäivita EDL\'i Uuendus saadaval @@ -68,8 +70,22 @@ Edenemise teated Allalaadimine valmis Faili allalaadimisel esines viga + Kuva ülemkaustas + Kuva fail Magisk\'ile on uuendus saadaval! Magisk Manager\'ile on uuendus saadaval! + + + Vajuta allalaadimiseks ja installimiseks. + Laadi ainult ZIP alla + Otsene install (soovitatud) + Installi ebaaktiivsesse lahtrisse (pärast üle-õhu uuendust) + Sinu seade SUNNITAKSE peale taaskäivitust käivituma praegusesse ebaaktiivsesse lahtrisse!\nKasuta seda valikut vaid peale üle-õhu uuenduse teostamist.\nJätkad? + Vali meetod + Lisaseadistus + Vali ja paika fail + Vali toortõmmis (*.img) või ODIN tar-fail (*.tar) + Taaskäivitamine 5 sekundi pärast… Sulge @@ -80,19 +96,16 @@ Taaskäivita seadete rakendamiseks. Väljalaske märkmed Hoidla vahemälu tühjendatud - Vajuta allalaadimiseks ja installimiseks. + DTBO sai paigatud! Magisk Manager on paiganud dtbo.img. Palun taaskäivita. Välgutamine + Valmis! + Ebaõnnestus Peidan Magisk Manager\'i… Magisk Manager\'i peitmine ebaõnnestus. Lingi avamiseks sobivat rakendust ei leitud. - Laadi ainult ZIP alla - Otsene install (soovitatud) - Installi ebaaktiivsesse lahtrisse (pärast üle-õhu uuendust) Hoiatus - Sinu seade SUNNITAKSE peale taaskäivitust käivituma praegusesse ebaaktiivsesse lahtrisse!\nKasuta seda valikut vaid peale üle-õhu uuenduse teostamist.\nJätkad? - Vali meetod Täielik eemaldus Taasta tõmmised Taastamine… @@ -103,13 +116,14 @@ Seadistus ebaõnnnestus. Vajab lisaseadistust Sinu seade vajab lisaseadistust, et Magisk töötaks korralikult. Laadime alla Magisk\'i seadistus-zip\'i, kas soovid kohe jätkata? - Lisaseadistus Käivitan keskkonnaseadistust… Üldine Tume teema Luba tume teema. + Allalaadimise failitee + Failid salvestatakse kausta %1$s Tühjenda hoidla vahemälu Tühjenda vahemälus olev teave võrgus olevate hoidlate kohta. See sunnib rakendust võrgust värskendama. Peida Magisk Manager @@ -171,6 +185,7 @@ Iga juurkasutaja sessioon saab oma isoleeritud nimeruumi. Ei toeta Androidi versiooni 8.0+. Sõrmejälgi pole määratud või seade pole toetatud. + Faili loomisel esines viga. See peab olema ligipääsetav mäluruumi juurkaustast ning ei tohi olla fail. Superkasutaja taotlus diff --git a/shared/src/main/res/values-et/strings.xml b/shared/src/main/res/values-et/strings.xml index 26bf11081..a8dd7ca3d 100644 --- a/shared/src/main/res/values-et/strings.xml +++ b/shared/src/main/res/values-et/strings.xml @@ -1,6 +1,4 @@ - - Tänan ei Jah diff --git a/stub/src/main/res/values-et/strings.xml b/stub/src/main/res/values-et/strings.xml index 13abe1f4a..ec72c8a82 100644 --- a/stub/src/main/res/values-et/strings.xml +++ b/stub/src/main/res/values-et/strings.xml @@ -1,5 +1,4 @@ - - Täienda seadistuse lõpetamiseks Magisk Manager täisversioonile. Kas laadin alla ja installin? - Palun ühendu Internetti! Nõutud on Magisk Manageri täiendamine. + Täienda seadistuse lõpetamiseks Magisk Manager\'i täisversioonile. Kas laadid alla ja installid? + Palun ühendu Internetti! Nõutud on Magisk Manager\'i täisversioonile täiendamine. From 4a45ba3c142a134337646177f68431697431910a Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 8 Oct 2019 14:53:04 -0400 Subject: [PATCH 31/50] Update magisk_files commit hashes --- app/src/main/java/com/topjohnwu/magisk/Const.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/Const.kt b/app/src/main/java/com/topjohnwu/magisk/Const.kt index 60ae1284e..1f6dd169f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Const.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Const.kt @@ -13,8 +13,8 @@ object Const { // Versions const val SNET_EXT_VER = 13 - const val SNET_REVISION = "5adbc435ce93ded953c30ebe587edfd50b5503bc" - const val BOOTCTL_REVISION = "9c5dfc1b8245c0b5b524901ef0ff0f8335757b77" + const val SNET_REVISION = "a6c47f86f10b310358afa9dbe837037dd5d561df" + const val BOOTCTL_REVISION = "a6c47f86f10b310358afa9dbe837037dd5d561df" // Misc const val ANDROID_MANIFEST = "AndroidManifest.xml" From da3fd92b318156362c6a56c25d9c291e289d8bf1 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 8 Oct 2019 15:54:54 -0400 Subject: [PATCH 32/50] Prevent unsigned overflow Close #1898 --- native/jni/magiskboot/bootimg.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/native/jni/magiskboot/bootimg.cpp b/native/jni/magiskboot/bootimg.cpp index a25185772..f0d3c59e5 100644 --- a/native/jni/magiskboot/bootimg.cpp +++ b/native/jni/magiskboot/bootimg.cpp @@ -183,19 +183,20 @@ void boot_img::parse_image(uint8_t *addr) { } void boot_img::find_kernel_dtb() { - for (int i = 0; i < hdr->kernel_size() - 4; ++i) { + const int eof = static_cast(hdr->kernel_size()); + for (int i = 0; i < eof - (int) sizeof(fdt_header); ++i) { auto fdt_hdr = reinterpret_cast(kernel + i); if (fdt32_to_cpu(fdt_hdr->magic) != FDT_MAGIC) continue; // Check that fdt_header.totalsize does not overflow kernel image size uint32_t totalsize = fdt32_to_cpu(fdt_hdr->totalsize); - if (totalsize + i > hdr->kernel_size()) + if (totalsize + i > eof) continue; // Check that fdt_header.off_dt_struct does not overflow kernel image size uint32_t off_dt_struct = fdt32_to_cpu(fdt_hdr->off_dt_struct); - if (off_dt_struct + i > hdr->kernel_size()) + if (off_dt_struct + i > eof) continue; // Check that fdt_node_header.tag of first node is FDT_BEGIN_NODE @@ -204,7 +205,7 @@ void boot_img::find_kernel_dtb() { continue; kernel_dtb = kernel + i; - kernel_dt_size = hdr->kernel_size() - i; + kernel_dt_size = eof - i; hdr->kernel_size() = i; fprintf(stderr, "KERNEL_DTB [%u]\n", kernel_dt_size); break; From c42a51dcbbed996aebc6bd3e184babaee45b140f Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 8 Oct 2019 16:43:27 -0400 Subject: [PATCH 33/50] Add support to patch DTBH DTBs Apparently, Qualcomm is not the only on creating weird DTB formats, Samsung also have their own DTBH format for Exynos platforms. Close #1902 --- native/jni/magiskboot/dtb.cpp | 29 +++++++++++++++++++++++++++-- native/jni/magiskboot/format.h | 1 + 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/native/jni/magiskboot/dtb.cpp b/native/jni/magiskboot/dtb.cpp index b1a009538..e9fef715b 100644 --- a/native/jni/magiskboot/dtb.cpp +++ b/native/jni/magiskboot/dtb.cpp @@ -41,6 +41,19 @@ struct qctable_v3 { uint32_t len; /* DTB size */ } __attribute__((packed)); +struct dtbh_hdr { + char magic[4]; /* "DTBH" */ + uint32_t version; /* DTBH version */ + uint32_t num_dtbs; /* Number of DTBs */ +} __attribute__((packed)); + +struct bhtable_v2 { + uint32_t cpu_info[5]; /* Some CPU info */ + uint32_t offset; /* DTB offset in DTBH */ + uint32_t len; /* DTB size */ + uint32_t space; /* 0x00000020 */ +}; + struct dtb_blob { void *fdt; uint32_t offset; @@ -215,8 +228,8 @@ static bool fdt_patch(Iter first, Iter last) { return modified; } -template -static int dtb_patch(const qcdt_hdr *hdr, const char *in, const char *out) { +template +static int dtb_patch(const Header *hdr, const char *in, const char *out) { map dtb_map; auto buf = reinterpret_cast(hdr); auto tables = reinterpret_cast(hdr + 1); @@ -295,14 +308,26 @@ static int dtb_patch(const char *in, const char *out) { auto hdr = reinterpret_cast(dtb); switch (hdr->version) { case 1: + fprintf(stderr, "QCDT v1\n"); return dtb_patch(hdr, in, out); case 2: + fprintf(stderr, "QCDT v2\n"); return dtb_patch(hdr, in, out); case 3: + fprintf(stderr, "QCDT v3\n"); return dtb_patch(hdr, in, out); default: return 1; } + } else if (memcmp(dtb, DTBH_MAGIC, 4) == 0) { + auto hdr = reinterpret_cast(dtb); + switch (hdr->version) { + case 2: + fprintf(stderr, "DTBH v2\n"); + return dtb_patch(hdr, in, out); + default: + return 1; + } } else { vector fdt_list; for (int i = 0; i < dtb_sz; ++i) { diff --git a/native/jni/magiskboot/format.h b/native/jni/magiskboot/format.h index 1552e5d7c..782cde54b 100644 --- a/native/jni/magiskboot/format.h +++ b/native/jni/magiskboot/format.h @@ -42,6 +42,7 @@ typedef enum { #define LG_BUMP_MAGIC "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79" #define DHTB_MAGIC "\x44\x48\x54\x42\x01\x00\x00\x00" #define QCDT_MAGIC "QCDT" +#define DTBH_MAGIC "DTBH" #define SEANDROID_MAGIC "SEANDROIDENFORCE" #define TEGRABLOB_MAGIC "-SIGNED-BY-SIGNBLOB-" #define NOOKHD_RL_MAGIC "Red Loader" From d25ae5e0a964c5cc951a129de2c63a7cd797aad7 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 8 Oct 2019 16:55:25 -0400 Subject: [PATCH 34/50] Add __attribute__((packed)) just in case --- native/jni/magiskboot/dtb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/jni/magiskboot/dtb.cpp b/native/jni/magiskboot/dtb.cpp index e9fef715b..5a8a98990 100644 --- a/native/jni/magiskboot/dtb.cpp +++ b/native/jni/magiskboot/dtb.cpp @@ -52,7 +52,7 @@ struct bhtable_v2 { uint32_t offset; /* DTB offset in DTBH */ uint32_t len; /* DTB size */ uint32_t space; /* 0x00000020 */ -}; +} __attribute__((packed)); struct dtb_blob { void *fdt; From f5d054b93c6ed30b0662fd6313142d94b77c967d Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 8 Oct 2019 23:49:21 -0400 Subject: [PATCH 35/50] Add support for PXA DTBs --- native/jni/magiskboot/dtb.cpp | 55 ++++++++++++++++++++++++++++---- native/jni/magiskboot/format.cpp | 4 --- native/jni/magiskboot/format.h | 3 -- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/native/jni/magiskboot/dtb.cpp b/native/jni/magiskboot/dtb.cpp index 5a8a98990..6ca6442af 100644 --- a/native/jni/magiskboot/dtb.cpp +++ b/native/jni/magiskboot/dtb.cpp @@ -10,12 +10,14 @@ extern "C" { #include #include "magiskboot.h" -#include "format.h" using namespace std; -constexpr int MAX_DEPTH = 32; -static bitset depth_set; +#define DTB_MAGIC "\xd0\x0d\xfe\xed" +#define QCDT_MAGIC "QCDT" +#define DTBH_MAGIC "DTBH" +#define PXADT_MAGIC "PXA-DT" +#define PXA_19xx_MAGIC "PXA-19xx" struct qcdt_hdr { char magic[4]; /* "QCDT" */ @@ -54,6 +56,24 @@ struct bhtable_v2 { uint32_t space; /* 0x00000020 */ } __attribute__((packed)); +struct pxadt_hdr { + char magic[6]; /* "PXA-DT" */ + uint32_t version; /* PXA-DT version */ + uint32_t num_dtbs; /* Number of DTBs */ +} __attribute__((packed)); + +struct pxa_19xx_hdr { + char magic[8]; /* "PXA-19xx" */ + uint32_t version; /* PXA-DT version */ + uint32_t num_dtbs; /* Number of DTBs */ +} __attribute__((packed)); + +struct pxa_table_v1 { + uint32_t cpu_info[2]; /* Some CPU info */ + uint32_t offset; /* DTB offset in PXA-DT */ + uint32_t len; /* DTB size */ +} __attribute__((packed)); + struct dtb_blob { void *fdt; uint32_t offset; @@ -83,6 +103,9 @@ private: template inline fdt_map_iter make_iter(Iter j) { return fdt_map_iter(j); } +constexpr int MAX_DEPTH = 32; +static bitset depth_set; + static void pretty_node(int depth) { if (depth == 0) return; @@ -282,7 +305,7 @@ static int dtb_patch(const Header *hdr, const char *in, const char *out) { } // Patch tables - auto tables_rw = reinterpret_cast
(addr + sizeof(qcdt_hdr)); + auto tables_rw = reinterpret_cast
(addr + sizeof(Header)); for (int i = 0; i < hdr->num_dtbs; ++i) { auto &blob = dtb_map[tables_rw[i].offset]; tables_rw[i].offset = blob.offset; @@ -295,6 +318,8 @@ static int dtb_patch(const Header *hdr, const char *in, const char *out) { return 0; } +#define MATCH(s) (memcmp(dtb, s, sizeof(s) - 1) == 0) + static int dtb_patch(const char *in, const char *out) { if (!out) out = in; @@ -304,7 +329,7 @@ static int dtb_patch(const char *in, const char *out) { mmap_ro(in, dtb, dtb_sz); run_finally f([&]{ munmap(dtb, dtb_sz); }); - if (memcmp(dtb, QCDT_MAGIC, 4) == 0) { + if (MATCH(QCDT_MAGIC)) { auto hdr = reinterpret_cast(dtb); switch (hdr->version) { case 1: @@ -319,7 +344,7 @@ static int dtb_patch(const char *in, const char *out) { default: return 1; } - } else if (memcmp(dtb, DTBH_MAGIC, 4) == 0) { + } else if (MATCH(DTBH_MAGIC)) { auto hdr = reinterpret_cast(dtb); switch (hdr->version) { case 2: @@ -328,6 +353,24 @@ static int dtb_patch(const char *in, const char *out) { default: return 1; } + } else if (MATCH(PXADT_MAGIC)) { + auto hdr = reinterpret_cast(dtb); + switch (hdr->version) { + case 1: + fprintf(stderr, "PXA-DT v1\n"); + return dtb_patch(hdr, in, out); + default: + return 1; + } + } else if (MATCH(PXA_19xx_MAGIC)) { + auto hdr = reinterpret_cast(dtb); + switch (hdr->version) { + case 1: + fprintf(stderr, "PXA-19xx v1\n"); + return dtb_patch(hdr, in, out); + default: + return 1; + } } else { vector fdt_list; for (int i = 0; i < dtb_sz; ++i) { diff --git a/native/jni/magiskboot/format.cpp b/native/jni/magiskboot/format.cpp index 7ed50a2fd..6d2d86b3a 100644 --- a/native/jni/magiskboot/format.cpp +++ b/native/jni/magiskboot/format.cpp @@ -44,8 +44,6 @@ format_t check_fmt(const void *buf, size_t len) { return LZ4_LEGACY; } else if (MATCH(MTK_MAGIC)) { return MTK; - } else if (MATCH(DTB_MAGIC)) { - return DTB; } else if (MATCH(DHTB_MAGIC)) { return DHTB; } else if (MATCH(TEGRABLOB_MAGIC)) { @@ -77,8 +75,6 @@ const char *Fmt2Name::operator[](format_t fmt) { return "lz4_legacy"; case MTK: return "mtk"; - case DTB: - return "dtb"; default: return "raw"; } diff --git a/native/jni/magiskboot/format.h b/native/jni/magiskboot/format.h index 782cde54b..6fa8c2a83 100644 --- a/native/jni/magiskboot/format.h +++ b/native/jni/magiskboot/format.h @@ -38,11 +38,8 @@ typedef enum { #define LZ41_MAGIC "\x03\x21\x4c\x18" #define LZ42_MAGIC "\x04\x22\x4d\x18" #define MTK_MAGIC "\x88\x16\x88\x58" -#define DTB_MAGIC "\xd0\x0d\xfe\xed" #define LG_BUMP_MAGIC "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79" #define DHTB_MAGIC "\x44\x48\x54\x42\x01\x00\x00\x00" -#define QCDT_MAGIC "QCDT" -#define DTBH_MAGIC "DTBH" #define SEANDROID_MAGIC "SEANDROIDENFORCE" #define TEGRABLOB_MAGIC "-SIGNED-BY-SIGNBLOB-" #define NOOKHD_RL_MAGIC "Red Loader" From 23c1a1dab8169c9c18bad7a0d78b7f345ea00c81 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 9 Oct 2019 16:01:21 -0400 Subject: [PATCH 36/50] Some code reorganizing --- native/jni/magiskboot/bootimg.cpp | 234 ++++++++++++++--------------- native/jni/magiskboot/bootimg.h | 22 +-- native/jni/magiskboot/magiskboot.h | 2 +- 3 files changed, 129 insertions(+), 129 deletions(-) diff --git a/native/jni/magiskboot/bootimg.cpp b/native/jni/magiskboot/bootimg.cpp index f0d3c59e5..c20f5aaaf 100644 --- a/native/jni/magiskboot/bootimg.cpp +++ b/native/jni/magiskboot/bootimg.cpp @@ -44,7 +44,7 @@ static void dump(void *buf, size_t size, const char *filename) { close(fd); } -static size_t restore(const char *filename, int fd) { +static size_t restore(int fd, const char *filename) { int ifd = xopen(filename, O_RDONLY); size_t size = lseek(ifd, 0, SEEK_END); lseek(ifd, 0, SEEK_SET); @@ -57,12 +57,102 @@ static void restore_buf(int fd, const void *buf, size_t size) { xwrite(fd, buf, size); } -boot_img::~boot_img() { - munmap(map_addr, map_size); - delete hdr; +void dyn_img_hdr::print() { + uint32_t ver = header_version(); + fprintf(stderr, "HEADER_VER [%u]\n", ver); + fprintf(stderr, "KERNEL_SZ [%u]\n", kernel_size()); + fprintf(stderr, "RAMDISK_SZ [%u]\n", ramdisk_size()); + fprintf(stderr, "SECOND_SZ [%u]\n", second_size()); + if (ver) { + fprintf(stderr, "RECOV_DTBO_SZ [%u]\n", recovery_dtbo_size()); + fprintf(stderr, "DTB [%u]\n", dtb_size()); + } else { + fprintf(stderr, "EXTRA_SZ [%u]\n", extra_size()); + } + + ver = os_version(); + if (ver) { + int a,b,c,y,m = 0; + int version, patch_level; + version = ver >> 11; + patch_level = ver & 0x7ff; + + a = (version >> 14) & 0x7f; + b = (version >> 7) & 0x7f; + c = version & 0x7f; + fprintf(stderr, "OS_VERSION [%d.%d.%d]\n", a, b, c); + + y = (patch_level >> 4) + 2000; + m = patch_level & 0xf; + fprintf(stderr, "OS_PATCH_LEVEL [%d-%02d]\n", y, m); + } + + fprintf(stderr, "PAGESIZE [%u]\n", page_size()); + fprintf(stderr, "NAME [%s]\n", name()); + fprintf(stderr, "CMDLINE [%.512s%.1024s]\n", cmdline(), extra_cmdline()); + fprintf(stderr, "CHECKSUM ["); + for (int i = 0; id()[i]; ++i) + fprintf(stderr, "%02x", id()[i]); + fprintf(stderr, "]\n"); } -void boot_img::parse_file(const char *image) { +void dyn_img_hdr::dump_hdr_file() { + FILE *fp = xfopen(HEADER_FILE, "w"); + fprintf(fp, "pagesize=%u\n", page_size()); + fprintf(fp, "name=%s\n", name()); + fprintf(fp, "cmdline=%.512s%.1024s\n", cmdline(), extra_cmdline()); + uint32_t ver = os_version(); + if (ver) { + int a, b, c, y, m = 0; + int version, patch_level; + version = ver >> 11; + patch_level = ver & 0x7ff; + + a = (version >> 14) & 0x7f; + b = (version >> 7) & 0x7f; + c = version & 0x7f; + fprintf(fp, "os_version=%d.%d.%d\n", a, b, c); + + y = (patch_level >> 4) + 2000; + m = patch_level & 0xf; + fprintf(fp, "os_patch_level=%d-%02d\n", y, m); + } + fclose(fp); +} + +void dyn_img_hdr::load_hdr_file() { + parse_prop_file(HEADER_FILE, [=](string_view key, string_view value) -> bool { + if (key == "page_size") { + page_size() = parse_int(value); + } else if (key == "name") { + memset(name(), 0, 16); + memcpy(name(), value.data(), value.length() > 15 ? 15 : value.length()); + } else if (key == "cmdline") { + memset(cmdline(), 0, 512); + memset(extra_cmdline(), 0, 1024); + if (value.length() > 512) { + memcpy(cmdline(), value.data(), 512); + memcpy(extra_cmdline(), &value[512], value.length() - 511); + } else { + memcpy(cmdline(), value.data(), value.length()); + } + } else if (key == "os_version") { + int patch_level = os_version() & 0x7ff; + int a, b, c; + sscanf(value.data(), "%d.%d.%d", &a, &b, &c); + os_version() = (((a << 14) | (b << 7) | c) << 11) | patch_level; + } else if (key == "os_patch_level") { + int os_ver = os_version() >> 11; + int y, m; + sscanf(value.data(), "%d-%d", &y, &m); + y -= 2000; + os_version() = (os_ver << 11) | (y << 4) | m; + } + return true; + }); +} + +boot_img::boot_img(const char *image) { mmap_ro(image, map_addr, map_size); fprintf(stderr, "Parsing boot image: [%s]\n", image); for (uint8_t *addr = map_addr; addr < map_addr + map_size; ++addr) { @@ -92,6 +182,11 @@ void boot_img::parse_file(const char *image) { exit(1); } +boot_img::~boot_img() { + munmap(map_addr, map_size); + delete hdr; +} + #define get_block(name) {\ name = addr + off; \ off += hdr->name##_size(); \ @@ -128,7 +223,7 @@ void boot_img::parse_image(uint8_t *addr) { img_start = addr; flags |= hdr->id()[SHA_DIGEST_SIZE] ? SHA256_FLAG : 0; - print_hdr(); + hdr->print(); size_t off = hdr->page_size(); @@ -212,107 +307,11 @@ void boot_img::find_kernel_dtb() { } } -void boot_img::print_hdr() { - uint32_t ver = hdr->header_version(); - fprintf(stderr, "HEADER_VER [%u]\n", ver); - fprintf(stderr, "KERNEL_SZ [%u]\n", hdr->kernel_size()); - fprintf(stderr, "RAMDISK_SZ [%u]\n", hdr->ramdisk_size()); - fprintf(stderr, "SECOND_SZ [%u]\n", hdr->second_size()); - if (ver) { - fprintf(stderr, "RECOV_DTBO_SZ [%u]\n", hdr->recovery_dtbo_size()); - fprintf(stderr, "DTB [%u]\n", hdr->dtb_size()); - } else { - fprintf(stderr, "EXTRA_SZ [%u]\n", hdr->extra_size()); - } - - ver = hdr->os_version(); - if (ver) { - int a,b,c,y,m = 0; - int version, patch_level; - version = ver >> 11; - patch_level = ver & 0x7ff; - - a = (version >> 14) & 0x7f; - b = (version >> 7) & 0x7f; - c = version & 0x7f; - fprintf(stderr, "OS_VERSION [%d.%d.%d]\n", a, b, c); - - y = (patch_level >> 4) + 2000; - m = patch_level & 0xf; - fprintf(stderr, "OS_PATCH_LEVEL [%d-%02d]\n", y, m); - } - - fprintf(stderr, "PAGESIZE [%u]\n", hdr->page_size()); - fprintf(stderr, "NAME [%s]\n", hdr->name()); - fprintf(stderr, "CMDLINE [%.512s%.1024s]\n", hdr->cmdline(), hdr->extra_cmdline()); - fprintf(stderr, "CHECKSUM ["); - for (int i = 0; hdr->id()[i]; ++i) - fprintf(stderr, "%02x", hdr->id()[i]); - fprintf(stderr, "]\n"); -} - -static void dump_hdr_file(dyn_img_hdr *hdr) { - FILE *fp = xfopen(HEADER_FILE, "w"); - fprintf(fp, "pagesize=%u\n", hdr->page_size()); - fprintf(fp, "name=%s\n", hdr->name()); - fprintf(fp, "cmdline=%.512s%.1024s\n", hdr->cmdline(), hdr->extra_cmdline()); - uint32_t ver = hdr->os_version(); - if (ver) { - int a, b, c, y, m = 0; - int version, patch_level; - version = ver >> 11; - patch_level = ver & 0x7ff; - - a = (version >> 14) & 0x7f; - b = (version >> 7) & 0x7f; - c = version & 0x7f; - fprintf(fp, "os_version=%d.%d.%d\n", a, b, c); - - y = (patch_level >> 4) + 2000; - m = patch_level & 0xf; - fprintf(fp, "os_patch_level=%d-%02d\n", y, m); - } - fclose(fp); -} - -static void load_hdr_file(dyn_img_hdr *hdr) { - parse_prop_file(HEADER_FILE, [=](string_view key, string_view value) -> bool { - if (key == "page_size") { - hdr->page_size() = parse_int(value); - } else if (key == "name") { - memset(hdr->name(), 0, 16); - memcpy(hdr->name(), value.data(), value.length() > 15 ? 15 : value.length()); - } else if (key == "cmdline") { - memset(hdr->cmdline(), 0, 512); - memset(hdr->extra_cmdline(), 0, 1024); - if (value.length() > 512) { - memcpy(hdr->cmdline(), value.data(), 512); - memcpy(hdr->extra_cmdline(), &value[512], value.length() - 511); - } else { - memcpy(hdr->cmdline(), value.data(), value.length()); - } - } else if (key == "os_version") { - int patch_level = hdr->os_version() & 0x7ff; - int a, b, c; - sscanf(value.data(), "%d.%d.%d", &a, &b, &c); - hdr->os_version() = (((a << 14) | (b << 7) | c) << 11) | patch_level; - } else if (key == "os_patch_level") { - int os_version = hdr->os_version() >> 11; - int y, m; - sscanf(value.data(), "%d-%d", &y, &m); - y -= 2000; - hdr->os_version() = (os_version << 11) | (y << 4) | m; - } - return true; - }); -} - int unpack(const char *image, bool hdr) { - boot_img boot{}; - boot.parse_file(image); + boot_img boot(image); if (hdr) - dump_hdr_file(boot.hdr); + boot.hdr->dump_hdr_file(); // Dump kernel if (COMPRESSED(boot.k_fmt)) { @@ -353,8 +352,8 @@ int unpack(const char *image, bool hdr) { #define file_align() \ write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR) - off.header, boot.hdr->page_size())) -void repack(const char* orig_image, const char* out_image, bool nocomp) { - boot_img boot{}; +void repack(const char* src_img, const char* out_img, bool nocomp) { + boot_img boot(src_img); struct { uint32_t header; @@ -365,10 +364,7 @@ void repack(const char* orig_image, const char* out_image, bool nocomp) { uint32_t dtb; } off; - // Parse original image - boot.parse_file(orig_image); - - fprintf(stderr, "Repack to boot image: [%s]\n", out_image); + fprintf(stderr, "Repack to boot image: [%s]\n", out_img); // Reset sizes boot.hdr->kernel_size() = 0; @@ -378,14 +374,14 @@ void repack(const char* orig_image, const char* out_image, bool nocomp) { boot.kernel_dt_size = 0; if (access(HEADER_FILE, R_OK) == 0) - load_hdr_file(boot.hdr); + boot.hdr->load_hdr_file(); /***************** * Writing blocks *****************/ // Create new image - int fd = creat(out_image, 0644); + int fd = creat(out_img, 0644); if (boot.flags & DHTB_FLAG) { // Skip DHTB header @@ -422,7 +418,7 @@ void repack(const char* orig_image, const char* out_image, bool nocomp) { // kernel dtb if (access(KER_DTB_FILE, R_OK) == 0) - boot.hdr->kernel_size() += restore(KER_DTB_FILE, fd); + boot.hdr->kernel_size() += restore(fd, KER_DTB_FILE); file_align(); // ramdisk @@ -447,28 +443,28 @@ void repack(const char* orig_image, const char* out_image, bool nocomp) { // second off.second = lseek(fd, 0, SEEK_CUR); if (access(SECOND_FILE, R_OK) == 0) { - boot.hdr->second_size() = restore(SECOND_FILE, fd); + boot.hdr->second_size() = restore(fd, SECOND_FILE); file_align(); } // extra off.extra = lseek(fd, 0, SEEK_CUR); if (access(EXTRA_FILE, R_OK) == 0) { - boot.hdr->extra_size() = restore(EXTRA_FILE, fd); + boot.hdr->extra_size() = restore(fd, EXTRA_FILE); file_align(); } // recovery_dtbo if (access(RECV_DTBO_FILE, R_OK) == 0) { boot.hdr->recovery_dtbo_offset() = lseek(fd, 0, SEEK_CUR); - boot.hdr->recovery_dtbo_size() = restore(RECV_DTBO_FILE, fd); + boot.hdr->recovery_dtbo_size() = restore(fd, RECV_DTBO_FILE); file_align(); } // dtb off.dtb = lseek(fd, 0, SEEK_CUR); if (access(DTB_FILE, R_OK) == 0) { - boot.hdr->dtb_size() = restore(DTB_FILE, fd); + boot.hdr->dtb_size() = restore(fd, DTB_FILE); file_align(); } @@ -488,7 +484,7 @@ void repack(const char* orig_image, const char* out_image, bool nocomp) { // Map output image as rw munmap(boot.map_addr, boot.map_size); - mmap_rw(out_image, boot.map_addr, boot.map_size); + mmap_rw(out_img, boot.map_addr, boot.map_size); // MTK headers if (boot.flags & MTK_KERNEL) { @@ -536,7 +532,7 @@ void repack(const char* orig_image, const char* out_image, bool nocomp) { (boot.flags & SHA256_FLAG) ? SHA256_DIGEST_SIZE : SHA_DIGEST_SIZE); // Print new image info - boot.print_hdr(); + boot.hdr->print(); // Main header memcpy(boot.map_addr + off.header, **boot.hdr, boot.hdr->hdr_size()); diff --git a/native/jni/magiskboot/bootimg.h b/native/jni/magiskboot/bootimg.h index ee34e6cd1..11d031453 100644 --- a/native/jni/magiskboot/bootimg.h +++ b/native/jni/magiskboot/bootimg.h @@ -192,6 +192,10 @@ struct dyn_img_hdr { return img_hdr; } + void print(); + void dump_hdr_file(); + void load_hdr_file(); + protected: union { /* Main header could be either AOSP or PXA, @@ -298,11 +302,11 @@ struct boot_img { uint8_t *map_addr; size_t map_size; - // Headers - dyn_img_hdr *hdr; /* Android image header */ + // Android image header + dyn_img_hdr *hdr; // Flags to indicate the state of current boot image - uint16_t flags; + uint16_t flags = 0; // The format of kernel and ramdisk format_t k_fmt; @@ -312,16 +316,17 @@ struct boot_img { * Following pointers points within the mmap region ***************************************************/ - mtk_hdr *k_hdr; /* MTK kernel header */ - mtk_hdr *r_hdr; /* MTK ramdisk header */ + // MTK headers + mtk_hdr *k_hdr; + mtk_hdr *r_hdr; // Pointer to dtb that is appended after kernel uint8_t *kernel_dtb; - uint32_t kernel_dt_size; + uint32_t kernel_dt_size = 0; // Pointer to end of image uint8_t *tail; - size_t tail_size; + size_t tail_size = 0; // Pointers to blocks defined in header uint8_t *img_start; @@ -332,10 +337,9 @@ struct boot_img { uint8_t *recovery_dtbo; uint8_t *dtb; + boot_img(const char *); ~boot_img(); - void parse_file(const char *); void parse_image(uint8_t *addr); void find_kernel_dtb(); - void print_hdr(); }; diff --git a/native/jni/magiskboot/magiskboot.h b/native/jni/magiskboot/magiskboot.h index cfbe0e151..6fb23cd09 100644 --- a/native/jni/magiskboot/magiskboot.h +++ b/native/jni/magiskboot/magiskboot.h @@ -13,7 +13,7 @@ #define NEW_BOOT "new-boot.img" int unpack(const char *image, bool hdr = false); -void repack(const char* orig_image, const char* out_image, bool nocomp = false); +void repack(const char* src_img, const char* out_img, bool nocomp = false); int hexpatch(const char *image, const char *from, const char *to); int cpio_commands(int argc, char *argv[]); int dtb_commands(int argc, char *argv[]); From 5e44b0b9d5125420265768f03c407c40dc729dcd Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 9 Oct 2019 17:38:45 -0400 Subject: [PATCH 37/50] Use raw literals for scripts --- native/jni/core/scripting.cpp | 45 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/native/jni/core/scripting.cpp b/native/jni/core/scripting.cpp index c0e30087d..ccb4e8da7 100644 --- a/native/jni/core/scripting.cpp +++ b/native/jni/core/scripting.cpp @@ -74,22 +74,24 @@ void exec_module_script(const char *stage, const vector &module_list) { } } -static const char migrate_script[] = -"IMG=%s;" -"MNT=/dev/img_mnt;" -"e2fsck -yf $IMG;" -"mkdir -p $MNT;" -"for num in 0 1 2 3 4 5 6 7; do" -" losetup /dev/block/loop${num} $IMG || continue;" -" mount -t ext4 /dev/block/loop${num} $MNT;" -" rm -rf $MNT/lost+found $MNT/.core;" -" magisk --clone $MNT " MODULEROOT ";" -" umount $MNT;" -" rm -rf $MNT;" -" losetup -d /dev/block/loop${num};" -" break;" -"done;" -"rm -rf $IMG;"; +constexpr char migrate_script[] = +"MODULEROOT=" MODULEROOT R"EOF( +IMG=%s +MNT=/dev/img_mnt +e2fsck -yf $IMG +mkdir -p $MNT +for num in 0 1 2 3 4 5 6 7; do + losetup /dev/block/loop${num} $IMG || continue + mount -t ext4 /dev/block/loop${num} $MNT + rm -rf $MNT/lost+found $MNT/.core + magisk --clone $MNT $MODULEROOT + umount $MNT + rm -rf $MNT + losetup -d /dev/block/loop${num} + break +done +rm -rf $IMG +)EOF"; void migrate_img(const char *img) { LOGI("* Migrating %s\n", img); @@ -99,11 +101,12 @@ void migrate_img(const char *img) { exec_command_sync(exec, "/system/bin/sh", "-c", cmds); } -static const char install_script[] = -"APK=%s;" -"log -t Magisk \"apk_install: $APK\";" -"log -t Magisk \"apk_install: `pm install -r $APK 2>&1`\";" -"rm -f $APK;"; +constexpr char install_script[] = R"EOF( +APK=%s +log -t Magisk "apk_install: $APK" +log -t Magisk "apk_install: `pm install -r $APK 2>&1`" +rm -f $APK +)EOF"; void install_apk(const char *apk) { setfilecon(apk, "u:object_r:" SEPOL_FILE_DOMAIN ":s0"); From 04920883ea431d71a6206a4321208aad3648ca0e Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 10 Oct 2019 15:07:45 -0400 Subject: [PATCH 38/50] Change code for handling tar files --- .../topjohnwu/magisk/tasks/MagiskInstaller.kt | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt b/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt index 9547adfb2..5357f9703 100644 --- a/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt +++ b/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt @@ -24,9 +24,11 @@ import org.kamranzafar.jtar.TarHeader import org.kamranzafar.jtar.TarInputStream import org.kamranzafar.jtar.TarOutputStream import timber.log.Timber -import java.io.* +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream import java.nio.ByteBuffer -import java.util.* import java.util.zip.ZipEntry import java.util.zip.ZipInputStream @@ -39,7 +41,7 @@ abstract class MagiskInstaller { private val console: MutableList private val logs: MutableList - private var isTar = false + private var tarOut: TarOutputStream? = null private val service: GithubRawServices by inject() private val context: Context by inject() @@ -150,7 +152,9 @@ abstract class MagiskInstaller { private fun handleTar(input: InputStream) { console.add("- Processing tar file") var vbmeta = false - withStreams(TarInputStream(input), TarOutputStream(destFile)) { tarIn, tarOut -> + val tarOut = TarOutputStream(destFile) + this.tarOut = tarOut + TarInputStream(input).use { tarIn -> lateinit var entry: TarEntry while (tarIn.nextEntry?.let { entry = it } != null) { if (entry.name.contains("boot.img") || entry.name.contains("recovery.img")) { @@ -214,8 +218,7 @@ abstract class MagiskInstaller { return false } it.reset() - if (Arrays.equals(magic, "ustar".toByteArray())) { - isTar = true + if (magic.contentEquals("ustar".toByteArray())) { destFile = File(Config.downloadDirectory, "magisk_patched.tar") handleTar(it) } else { @@ -292,15 +295,13 @@ abstract class MagiskInstaller { protected fun storeBoot(): Boolean { val patched = SuFile.open(installDir, "new-boot.img") try { - val os: OutputStream - if (isTar) { - os = TarOutputStream(destFile, true) - os.putNextEntry(newEntry( + val os = tarOut?.let { + it.putNextEntry(newEntry( if (srcBoot.contains("recovery")) "recovery.img" else "boot.img", patched.length())) - } else { - os = destFile.outputStream() - } + tarOut = null + it + } ?: destFile.outputStream() patched.suInputStream().use { it.copyTo(os); os.close() } } catch (e: IOException) { console.add("! Failed to output to $destFile") From 175d920c9499b73880c05132049d919c2cec731b Mon Sep 17 00:00:00 2001 From: dark-basic Date: Tue, 8 Oct 2019 18:52:42 -0300 Subject: [PATCH 39/50] Update strings.xml I'M BACK. New translations were added. --- app/src/main/res/values-es/strings.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index c6478bf23..44722a4d6 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -24,6 +24,7 @@ Ajustes avanzados Mantener cifrado forzado Mantener AVB 2.0/dm-verity + Modo Recovery Instalada: %1$s Última: %1$s Desinstalar @@ -44,6 +45,7 @@ Reiniciar en Modo Recovery Reiniciar en Modo Bootloader Reiniciar en Modo Download + Reiniciar en Modo EDL Actualización Disponible @@ -77,7 +79,7 @@ Instalación Directa (Recomendado) Instalar en ranura inactiva (después de OTA) ¡Se forzará su dispositivo para que arranque en la ranura inactiva actual después de un reinicio!\nUtilice esta opción solo después de que se haya completado la OTA.\nContinuar? - Seleccionar Método + Seleccionar Método Configuración Adicional Seleccionar y parchear un archivo Seleccione una imagen raw (* .img) o un archivo tar de ODIN (* .tar) @@ -118,6 +120,8 @@ General Tema oscuro Habilitar el tema oscuro + Ruta de Descarga + Los archivos se guardarán en %1$s Limpiar caché del repositorio Limpiar la información en caché para los repositorios en línea, fuerza a la aplicación a actualizar en línea Ocultar Magisk Manager @@ -178,6 +182,7 @@ Cada sesión root tendrá su propia Namespace No es compatible con Android 8.0+ No se establecieron huellas dactilares o no existe soporte del dispositivo + Error al crear la carpeta. Debe ser accesible desde el directorio raíz de almacenamiento y no debe ser un archivo. Petición de superusuario From c3e00c279d87463174db4d77ef98465c30c98d3b Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 11 Oct 2019 01:45:06 -0400 Subject: [PATCH 40/50] Legacy adb shell does not have uname --- scripts/emulator.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/emulator.sh b/scripts/emulator.sh index 077da4daa..af7be4a6a 100755 --- a/scripts/emulator.sh +++ b/scripts/emulator.sh @@ -31,7 +31,7 @@ if [ ! -f /system/build.prop ]; then cd "`dirname "$0"`/.." adb push native/out/x86/busybox scripts/emulator.sh /data/local/tmp emu_arch=`adb shell uname -m` - if [ $emu_arch = "x86_64" ]; then + if [ "$emu_arch" = "x86_64" ]; then adb push native/out/x86/magiskinit64 /data/local/tmp/magiskinit else adb push native/out/x86/magiskinit /data/local/tmp From 674d272eaad79bec58f8e14dedf8506fa5d67101 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 11 Oct 2019 01:46:15 -0400 Subject: [PATCH 41/50] Support pre-5.0 without GMS Fix #1912 --- app/build.gradle | 2 +- app/src/main/java/com/topjohnwu/magisk/App.kt | 3 --- .../topjohnwu/magisk/di/NetworkingModule.kt | 22 +++++++++++-------- shared/build.gradle | 1 + shared/proguard-rules.pro | 4 ++++ shared/src/main/AndroidManifest.xml | 1 + .../com/topjohnwu/magisk/net/Networking.java | 10 ++++++--- .../magisk/net/NoSSLv3SocketFactory.java | 20 ++++++++--------- 8 files changed, 37 insertions(+), 26 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b20bfe555..626847d6d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -94,7 +94,7 @@ dependencies { implementation "com.squareup.retrofit2:converter-scalars:${vRetrofit}" implementation "com.squareup.retrofit2:adapter-rxjava2:${vRetrofit}" - def vOkHttp = '3.12.2' + def vOkHttp = '3.12.6' implementation "com.squareup.okhttp3:okhttp:${vOkHttp}" implementation "com.squareup.okhttp3:logging-interceptor:${vOkHttp}" diff --git a/app/src/main/java/com/topjohnwu/magisk/App.kt b/app/src/main/java/com/topjohnwu/magisk/App.kt index 1224d400f..b742d5175 100644 --- a/app/src/main/java/com/topjohnwu/magisk/App.kt +++ b/app/src/main/java/com/topjohnwu/magisk/App.kt @@ -13,7 +13,6 @@ import com.topjohnwu.magisk.data.database.RepoDatabase_Impl import com.topjohnwu.magisk.di.ActivityTracker import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.magisk.extensions.get -import com.topjohnwu.magisk.net.Networking import com.topjohnwu.magisk.utils.LocaleManager import com.topjohnwu.magisk.utils.RootUtils import com.topjohnwu.superuser.Shell @@ -50,8 +49,6 @@ open class App : Application() { } registerActivityLifecycleCallbacks(get()) - - Networking.init(base) LocaleManager.setLocale(this) } diff --git a/app/src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt b/app/src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt index 3824a0c97..b814eb375 100644 --- a/app/src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt +++ b/app/src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt @@ -1,11 +1,14 @@ package com.topjohnwu.magisk.di +import android.content.Context import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.data.network.GithubApiServices import com.topjohnwu.magisk.data.network.GithubRawServices +import com.topjohnwu.magisk.net.Networking +import com.topjohnwu.magisk.net.NoSSLv3SocketFactory import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.koin.dsl.module @@ -16,14 +19,14 @@ import retrofit2.converter.scalars.ScalarsConverterFactory import se.ansman.kotshi.KotshiJsonAdapterFactory val networkingModule = module { - single { createOkHttpClient() } - single { createMoshiConverterFactory() } - single { createRetrofit(get(), get()) } + single { createOkHttpClient(get()) } + single { createRetrofit(get()) } single { createApiService(get(), Const.Url.GITHUB_RAW_URL) } single { createApiService(get(), Const.Url.GITHUB_API_URL) } } -fun createOkHttpClient(): OkHttpClient { +@Suppress("DEPRECATION") +fun createOkHttpClient(context: Context): OkHttpClient { val builder = OkHttpClient.Builder() if (BuildConfig.DEBUG) { @@ -33,6 +36,10 @@ fun createOkHttpClient(): OkHttpClient { builder.addInterceptor(httpLoggingInterceptor) } + if (!Networking.init(context)) { + builder.sslSocketFactory(NoSSLv3SocketFactory()) + } + return builder.build() } @@ -43,13 +50,10 @@ fun createMoshiConverterFactory(): MoshiConverterFactory { return MoshiConverterFactory.create(moshi) } -fun createRetrofit( - okHttpClient: OkHttpClient, - converterFactory: MoshiConverterFactory -): Retrofit.Builder { +fun createRetrofit(okHttpClient: OkHttpClient): Retrofit.Builder { return Retrofit.Builder() .addConverterFactory(ScalarsConverterFactory.create()) - .addConverterFactory(converterFactory) + .addConverterFactory(createMoshiConverterFactory()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .client(okHttpClient) } diff --git a/shared/build.gradle b/shared/build.gradle index bb94b657e..7251be266 100644 --- a/shared/build.gradle +++ b/shared/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'com.android.library' android { defaultConfig { vectorDrawables.useSupportLibrary = true + consumerProguardFiles 'proguard-rules.pro' } } diff --git a/shared/proguard-rules.pro b/shared/proguard-rules.pro index f1b424510..e0a7aa840 100644 --- a/shared/proguard-rules.pro +++ b/shared/proguard-rules.pro @@ -19,3 +19,7 @@ # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile + +-keepclassmembers class * implements javax.net.ssl.SSLSocketFactory { + ** delegate; +} diff --git a/shared/src/main/AndroidManifest.xml b/shared/src/main/AndroidManifest.xml index 9f68eba7a..82038fd93 100644 --- a/shared/src/main/AndroidManifest.xml +++ b/shared/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ + diff --git a/shared/src/main/java/com/topjohnwu/magisk/net/Networking.java b/shared/src/main/java/com/topjohnwu/magisk/net/Networking.java index 10cbee3dc..c23961340 100644 --- a/shared/src/main/java/com/topjohnwu/magisk/net/Networking.java +++ b/shared/src/main/java/com/topjohnwu/magisk/net/Networking.java @@ -35,7 +35,7 @@ public class Networking { return request(url, "GET"); } - public static void init(Context context) { + public static boolean init(Context context) { try { // Try installing new SSL provider from Google Play Service Context gms = context.createPackageContext("com.google.android.gms", @@ -45,10 +45,14 @@ public class Networking { .getMethod("insertProvider", Context.class) .invoke(null, gms); } catch (Exception e) { - // Failed to update SSL provider, use NoSSLv3SocketFactory on SDK < 21 - if (Build.VERSION.SDK_INT < 21) + if (Build.VERSION.SDK_INT < 21) { + // Failed to update SSL provider, use NoSSLv3SocketFactory on SDK < 21 + // and return false to notify potential issues HttpsURLConnection.setDefaultSSLSocketFactory(new NoSSLv3SocketFactory()); + return false; + } } + return true; } public static boolean checkNetworkStatus(Context context) { diff --git a/shared/src/main/java/com/topjohnwu/magisk/net/NoSSLv3SocketFactory.java b/shared/src/main/java/com/topjohnwu/magisk/net/NoSSLv3SocketFactory.java index b7a5774f2..c55bf4761 100644 --- a/shared/src/main/java/com/topjohnwu/magisk/net/NoSSLv3SocketFactory.java +++ b/shared/src/main/java/com/topjohnwu/magisk/net/NoSSLv3SocketFactory.java @@ -11,18 +11,18 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; -class NoSSLv3SocketFactory extends SSLSocketFactory { +public class NoSSLv3SocketFactory extends SSLSocketFactory { - private final static SSLSocketFactory base = HttpsURLConnection.getDefaultSSLSocketFactory(); + private final static SSLSocketFactory delegate = HttpsURLConnection.getDefaultSSLSocketFactory(); @Override public String[] getDefaultCipherSuites() { - return base.getDefaultCipherSuites(); + return delegate.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { - return base.getSupportedCipherSuites(); + return delegate.getSupportedCipherSuites(); } private Socket createSafeSocket(Socket socket) { @@ -40,31 +40,31 @@ class NoSSLv3SocketFactory extends SSLSocketFactory { @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { - return createSafeSocket(base.createSocket(s, host, port, autoClose)); + return createSafeSocket(delegate.createSocket(s, host, port, autoClose)); } @Override public Socket createSocket() throws IOException { - return createSafeSocket(base.createSocket()); + return createSafeSocket(delegate.createSocket()); } @Override public Socket createSocket(String host, int port) throws IOException { - return createSafeSocket(base.createSocket(host, port)); + return createSafeSocket(delegate.createSocket(host, port)); } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { - return createSafeSocket(base.createSocket(host, port, localHost, localPort)); + return createSafeSocket(delegate.createSocket(host, port, localHost, localPort)); } @Override public Socket createSocket(InetAddress host, int port) throws IOException { - return createSafeSocket(base.createSocket(host, port)); + return createSafeSocket(delegate.createSocket(host, port)); } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { - return createSafeSocket(base.createSocket(address, port, localAddress, localPort)); + return createSafeSocket(delegate.createSocket(address, port, localAddress, localPort)); } } From ff8460b36180f3edac9c6170ce811b796a26ba43 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 11 Oct 2019 03:29:55 -0400 Subject: [PATCH 42/50] Update dependencies --- app/build.gradle | 26 +++++++++-------- .../topjohnwu/magisk/di/NetworkingModule.kt | 16 ++++++++++- .../topjohnwu/magisk/view/MarkDownWindow.kt | 28 +++++-------------- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 626847d6d..1efd50a37 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,8 +63,9 @@ dependencies { implementation 'com.ncapdevi:frag-nav:3.2.0' implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.6' - implementation "io.reactivex.rxjava2:rxjava:2.2.12" - implementation "io.reactivex.rxjava2:rxkotlin:2.4.0" + implementation 'io.reactivex.rxjava2:rxjava:2.2.13' + implementation 'io.reactivex.rxjava2:rxkotlin:2.4.0' + implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation "org.jetbrains.kotlin:kotlin-stdlib:${vKotlin}" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${vKotlin}" @@ -74,10 +75,11 @@ dependencies { implementation "${bindingAdapter}:${vBAdapt}" implementation "${bindingAdapter}-recyclerview:${vBAdapt}" - def vMarkwon = '3.1.0' - implementation "ru.noties.markwon:core:${vMarkwon}" - implementation "ru.noties.markwon:html:${vMarkwon}" - implementation "ru.noties.markwon:image-svg:${vMarkwon}" + def vMarkwon = '4.1.1' + implementation "io.noties.markwon:core:${vMarkwon}" + implementation "io.noties.markwon:html:${vMarkwon}" + implementation "io.noties.markwon:image:${vMarkwon}" + implementation 'com.caverock:androidsvg:1.4' def vLibsu = '2.5.1' implementation "com.github.topjohnwu.libsu:core:${vLibsu}" @@ -88,7 +90,7 @@ dependencies { implementation "org.koin:koin-android:${vKoin}" implementation "org.koin:koin-androidx-viewmodel:${vKoin}" - def vRetrofit = '2.6.1' + def vRetrofit = '2.6.2' implementation "com.squareup.retrofit2:retrofit:${vRetrofit}" implementation "com.squareup.retrofit2:converter-moshi:${vRetrofit}" implementation "com.squareup.retrofit2:converter-scalars:${vRetrofit}" @@ -110,7 +112,7 @@ dependencies { replacedBy('com.github.topjohnwu:room-runtime') } } - def vRoom = "2.1.0" + def vRoom = "2.2.0" implementation "com.github.topjohnwu:room-runtime:${vRoom}" kapt "androidx.room:room-compiler:${vRoom}" @@ -119,13 +121,13 @@ dependencies { implementation "androidx.navigation:navigation-ui-ktx:$vNav" implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02' + implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03' implementation 'androidx.preference:preference:1.1.0' - implementation 'androidx.recyclerview:recyclerview:1.1.0-beta04' + implementation 'androidx.recyclerview:recyclerview:1.1.0-beta05' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.work:work-runtime:2.2.0' - implementation 'androidx.transition:transition:1.2.0-rc01' + implementation 'androidx.transition:transition:1.2.0' implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.core:core-ktx:1.1.0' - implementation 'com.google.android.material:material:1.1.0-alpha10' + implementation 'com.google.android.material:material:1.1.0-beta01' } diff --git a/app/src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt b/app/src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt index b814eb375..73db91cb9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt +++ b/app/src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt @@ -9,6 +9,10 @@ import com.topjohnwu.magisk.data.network.GithubApiServices import com.topjohnwu.magisk.data.network.GithubRawServices import com.topjohnwu.magisk.net.Networking import com.topjohnwu.magisk.net.NoSSLv3SocketFactory +import io.noties.markwon.Markwon +import io.noties.markwon.html.HtmlPlugin +import io.noties.markwon.image.ImagesPlugin +import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.koin.dsl.module @@ -23,6 +27,7 @@ val networkingModule = module { single { createRetrofit(get()) } single { createApiService(get(), Const.Url.GITHUB_RAW_URL) } single { createApiService(get(), Const.Url.GITHUB_API_URL) } + single { createMarkwon(get(), get()) } } @Suppress("DEPRECATION") @@ -66,4 +71,13 @@ inline fun createApiService(retrofitBuilder: Retrofit.Builder, baseU .baseUrl(baseUrl) .build() .create(T::class.java) -} \ No newline at end of file +} + +fun createMarkwon(context: Context, okHttpClient: OkHttpClient): Markwon { + return Markwon.builder(context) + .usePlugin(HtmlPlugin.create()) + .usePlugin(ImagesPlugin.create { + it.addSchemeHandler(OkHttpNetworkSchemeHandler.create(okHttpClient)) + }) + .build() +} diff --git a/app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.kt b/app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.kt index a7f16318e..071913a63 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.kt @@ -6,21 +6,20 @@ import android.widget.TextView import androidx.appcompat.app.AlertDialog import com.topjohnwu.magisk.R import com.topjohnwu.magisk.data.repository.StringRepository -import com.topjohnwu.magisk.extensions.inject import com.topjohnwu.magisk.extensions.subscribeK +import io.noties.markwon.Markwon import io.reactivex.Completable import io.reactivex.Single -import ru.noties.markwon.Markwon -import ru.noties.markwon.html.HtmlPlugin -import ru.noties.markwon.image.ImagesPlugin -import ru.noties.markwon.image.svg.SvgPlugin +import org.koin.core.KoinComponent +import org.koin.core.inject import timber.log.Timber import java.io.InputStream import java.util.* -object MarkDownWindow { +object MarkDownWindow : KoinComponent { private val stringRepo: StringRepository by inject() + private val markwon: Markwon by inject() fun show(activity: Context, title: String?, url: String) { show(activity, title, stringRepo.getString(url)) @@ -35,25 +34,14 @@ object MarkDownWindow { } fun show(activity: Context, title: String?, content: Single) { - val markwon = Markwon.builder(activity) - .usePlugin(HtmlPlugin.create()) - .usePlugin(ImagesPlugin.create(activity)) - .usePlugin(SvgPlugin.create(activity.resources)) - .build() val mv = LayoutInflater.from(activity).inflate(R.layout.markdown_window, null) val tv = mv.findViewById(R.id.md_txt) content.map { - runCatching { - markwon.setMarkdown(tv, it) - }.onFailure { - Timber.e(it) - // Always wrap the actual exception as it could be ExceptionInInitializerError, - // which is a fatal error and RxJava will send it to the global handler and crash - throw MarkwonException(it) - } + markwon.setMarkdown(tv, it) }.ignoreElement().onErrorResumeNext { // Nothing we can actually do other than show error message + Timber.e(it) tv.setText(R.string.download_file_error) Completable.complete() }.subscribeK { @@ -64,6 +52,4 @@ object MarkDownWindow { .show() } } - - class MarkwonException(e: Throwable): Exception(e) } From 06dc6df2705ca7fed9f83f82e8ad897d67910e99 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 11 Oct 2019 03:58:04 -0400 Subject: [PATCH 43/50] Allow dalvik runtime to load snet --- .../main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt index a35207f61..2cee4cbd8 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt @@ -9,7 +9,6 @@ import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.databinding.FragmentMagiskBinding -import com.topjohnwu.magisk.extensions.inject import com.topjohnwu.magisk.extensions.openUrl import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.writeTo @@ -21,6 +20,7 @@ import com.topjohnwu.magisk.view.dialogs.* import com.topjohnwu.superuser.Shell import dalvik.system.DexFile import io.reactivex.Completable +import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel import java.io.File import java.lang.reflect.InvocationHandler @@ -33,6 +33,7 @@ class HomeFragment : BaseFragment(), private val magiskRepo: MagiskRepository by inject() private val EXT_APK by lazy { File("${activity.filesDir.parent}/snet", "snet.jar") } + private val EXT_DEX by lazy { File(EXT_APK.parent, "snet.dex") } override fun onResponse(responseCode: Int) = viewModel.finishSafetyNetCheck(responseCode) @@ -94,7 +95,7 @@ class HomeFragment : BaseFragment(), private fun updateSafetyNet(dieOnError: Boolean) { Completable.fromAction { val loader = DynamicClassLoader(EXT_APK) - val dex = DexFile.loadDex(EXT_APK.path, EXT_APK.parent, 0) + val dex = DexFile.loadDex(EXT_APK.path, EXT_DEX.path, 0) // Scan through the dex and find our helper class var helperClass: Class<*>? = null From 59fd38bbf810c81076a1f9b16bc5a0071581b9e7 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 11 Oct 2019 16:12:32 -0400 Subject: [PATCH 44/50] Add v7.3.5 changelog --- app/src/main/res/raw/changelog.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/raw/changelog.md b/app/src/main/res/raw/changelog.md index 367db5bf5..dd0d950b3 100644 --- a/app/src/main/res/raw/changelog.md +++ b/app/src/main/res/raw/changelog.md @@ -1,4 +1,4 @@ -# v7.3.4 -- App is now fully written in Kotlin! -- New downloading system -- Add new "Recovery Mode" to Advanced Settings +# v7.3.5 +- Sort installed modules by name +- Better pre-5.0 support +- Fix potential issues when patching tar files From de969a9dab5a591b434bc731d5c4355bdc265b14 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 12 Oct 2019 00:53:04 -0400 Subject: [PATCH 45/50] Downgrade recyclerview --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 1efd50a37..0b8281060 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -123,7 +123,7 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03' implementation 'androidx.preference:preference:1.1.0' - implementation 'androidx.recyclerview:recyclerview:1.1.0-beta05' + implementation 'androidx.recyclerview:recyclerview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.work:work-runtime:2.2.0' implementation 'androidx.transition:transition:1.2.0' From f3d7f8506388b06fd96fc6343ae5a8d56ae2b2e3 Mon Sep 17 00:00:00 2001 From: Simon Shi Date: Sat, 12 Oct 2019 10:26:49 +0800 Subject: [PATCH 46/50] Fix incorrect link path for /sbin/.core --- native/jni/core/bootstages.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index ce7fc1941..fb80491ae 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -347,7 +347,7 @@ static bool magisk_env() { unlink("/data/magisk_debug.log"); // Backwards compatibility - symlink("./magisk", "/sbin/.core"); + symlink("./.magisk", "/sbin/.core"); symlink("./modules", MAGISKTMP "/img"); // Directories in tmpfs overlay From b05b68826799ee104288e0f137c963d8761f8a5b Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 12 Oct 2019 03:57:56 -0400 Subject: [PATCH 47/50] Fix issues in stub APK --- stub/src/main/AndroidManifest.xml | 7 +------ .../com/topjohnwu/magisk/BootLauncher.java | 16 ---------------- .../com/topjohnwu/magisk/MainActivity.java | 19 +++++++++++++------ 3 files changed, 14 insertions(+), 28 deletions(-) delete mode 100644 stub/src/main/java/com/topjohnwu/magisk/BootLauncher.java diff --git a/stub/src/main/AndroidManifest.xml b/stub/src/main/AndroidManifest.xml index 96be5b68d..6a0bfc6d1 100644 --- a/stub/src/main/AndroidManifest.xml +++ b/stub/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ package="com.topjohnwu.magisk"> + @@ -16,11 +17,5 @@ - - - - - - diff --git a/stub/src/main/java/com/topjohnwu/magisk/BootLauncher.java b/stub/src/main/java/com/topjohnwu/magisk/BootLauncher.java deleted file mode 100644 index 39390d026..000000000 --- a/stub/src/main/java/com/topjohnwu/magisk/BootLauncher.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.topjohnwu.magisk; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.text.TextUtils; - -public class BootLauncher extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (TextUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED)) { - Intent i = new Intent(context, MainActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(i); - } - } -} diff --git a/stub/src/main/java/com/topjohnwu/magisk/MainActivity.java b/stub/src/main/java/com/topjohnwu/magisk/MainActivity.java index 4c0be34d8..2d6e8b325 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/MainActivity.java +++ b/stub/src/main/java/com/topjohnwu/magisk/MainActivity.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.Application; import android.os.Bundle; +import android.util.Log; import com.topjohnwu.magisk.utils.APKInstall; import com.topjohnwu.magisk.net.Networking; @@ -16,16 +17,19 @@ import java.io.File; public class MainActivity extends Activity { + private static final String TAG = "MMStub"; + private static final boolean IS_CANARY = BuildConfig.VERSION_NAME.contains("-"); private static final String URL = - "https://raw.githubusercontent.com/topjohnwu/magisk_files/master/" + - (BuildConfig.VERSION_NAME.contains("-") ? "canary_builds/release.json" : "stable.json"); + "https://raw.githubusercontent.com/topjohnwu/magisk_files/" + + (IS_CANARY ? "canary/release.json" : "master/stable.json"); private String apkLink; private void dlAPK() { Application app = getApplication(); Networking.get(apkLink) - .getAsFile(new File(getFilesDir(), "manager.apk"), apk -> APKInstall.install(app, apk)); + .getAsFile(new File(getFilesDir(), "manager.apk"), + apk -> APKInstall.install(app, apk)); finish(); } @@ -35,10 +39,13 @@ public class MainActivity extends Activity { Networking.init(this); if (Networking.checkNetworkStatus(this)) { Networking.get(URL) - .setErrorHandler(((conn, e) -> finish())) + .setErrorHandler(((conn, e) -> { + Log.d(TAG, "network error", e); + finish(); + })) .getAsJSONObject(new JSONLoader()); } else { - new AlertDialog.Builder(this, AlertDialog.THEME_DEVICE_DEFAULT_LIGHT) + new AlertDialog.Builder(this) .setCancelable(false) .setTitle(R.string.app_name) .setMessage(R.string.no_internet_msg) @@ -54,7 +61,7 @@ public class MainActivity extends Activity { try { JSONObject manager = json.getJSONObject("app"); apkLink = manager.getString("link"); - new AlertDialog.Builder(MainActivity.this, AlertDialog.THEME_DEVICE_DEFAULT_LIGHT) + new AlertDialog.Builder(MainActivity.this) .setCancelable(false) .setTitle(R.string.app_name) .setMessage(R.string.upgrade_msg) From 5ffb9eaa5b90f736cc0de30fa6fbba2e6e950130 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 14 Oct 2019 03:49:17 -0400 Subject: [PATCH 48/50] Support loading Magisk Manager from stub on 9.0+ In the effort of preventing apps from crawling APK contents across the whole installed app list to detect Magisk Manager, the solution here is to NOT install the actual APK into the system, but instead dynamically load the full app at runtime by a stub app. The full APK will be stored in the application's private internal data where non-root processes cannot read or scan. The basis of this implementation is the class "AppComponentFactory" that is introduced in API 28. If assigned, the system framework will delegate app component instantiation to our custom implementation, which allows us to do all sorts of crazy stuffs, in our case dynamically load classes and create objects that does not exist in our APK. There are a few challenges to achieve our goal though. First, Java ClassLoaders follow the "delegation pattern", which means class loading resolution will first be delegated to the parent loader before we get a chance to do anything. This includes DexClassLoader, which is what we will be using to load DEX files at runtime. This is a problem because our stub app and full app share quite a lot of class names. A custom ClassLoader, DynamicClassLoader, is created to overcome this issue: it will always load classes in its current dex path before delegating it to the parent. Second, all app components (with the exception of runtime BroadcastReceivers) are required to be declared in AndroidManifest.xml. The full Magisk Manager has quite a lot of components (including those from WorkManager and Room). The solution is to copy the complete AndroidManifest.xml from the full app to the stub, and our AppComponentFactory is responsible to construct the proper objects or return dummy implementations in case the full APK isn't downloaded yet. Third, other than classes, all resources required to run the full app are also not bundled with the stub APK. We have to call an internal API `AssetManager.addAssetPath(String)` to add our downloaded full APK into AssetManager in order to access resources within our full app. That internal API has existed forever, and is whitelisted from restricted API access on modern Android versions, so it is pretty safe to use. Fourth, on the subject of resources, some resources are not just being used by our app at runtime. Resources such as the app icon, app label, launch theme, basically everything referred in AndroidManifest.xml, are used by the system to display the app properly. The system get these resources via resource IDs and direct loading from the installed APK. This subset of resources would have to be copied into the stub to make the app work properly. Fifth, resource IDs are used all over the place in XMLs and Java code. The resource IDs in the stub and full app cannot missmatch, or somewhere, either it be the system or AssetManager, will refer to the incorrect resource. The full app will have to include all resources in the stub, and all of them have to be assigned to the exact same IDs in both APKs. To achieve this, we use AAPT2's "--emit-ids" option to dump the resource ID mapping when building the stub, and "--stable-ids" when building the full APK to make sure all overlapping resources in full and stub are always assigned to the same ID. Finally, both stub and full app have to work properly independently. On 9.0+, the stub will have to first launch an Activity to download the full APK before it can relaunch into the full app. On pre-9.0, the stub should behave as it always did: download and prompt installation to upgrade itself to full Magisk Manager. In the full app, the goal is to introduce minimal intrusion to the code base to make sure this whole thing is maintainable in the future. Fortunately, the solution ends up pretty slick: all ContextWrappers in the app will be injected with custom Contexts. The custom Contexts will return our patched Resources object and the ClassLoader that loads itself, which will be DynamicClassLoader in the case of running as a delegate app. By directly patching the base Context of ContextWrappers (which covers tons of app components) and in the Koin DI, the effect propagates deep into every aspect of the code, making this change basically fully transparent to almost every piece of code in full Magisk Manager. After this commit, the stub app is able to properly download and launch the full app, with most basic functionalities working just fine. Do not expect Magisk Manager upgrades and hiding (repackaging) to work properly, and some other minor issues might pop up. This feature is still in the early WIP stages. --- README.MD | 1 - app/src/main/AndroidManifest.xml | 51 ++++-- app/src/main/java/a/a.java | 12 +- app/src/main/java/a/w.java | 3 +- app/src/main/java/com/topjohnwu/magisk/App.kt | 41 ++++- .../com/topjohnwu/magisk/base/BaseActivity.kt | 9 +- .../com/topjohnwu/magisk/base/BaseReceiver.kt | 17 ++ .../com/topjohnwu/magisk/base/BaseService.kt | 12 ++ .../topjohnwu/magisk/extensions/XAndroid.kt | 118 ++++++++++++ .../magisk/model/download/ManagerUpgrade.kt | 6 +- .../model/download/NotificationService.kt | 4 +- .../magisk/model/receiver/GeneralReceiver.kt | 10 +- .../com/topjohnwu/magisk/ui/MainActivity.kt | 4 +- .../com/topjohnwu/magisk/ui/SplashActivity.kt | 10 +- .../magisk/ui/flash/FlashActivity.kt | 1 + .../topjohnwu/magisk/ui/home/HomeFragment.kt | 2 +- .../magisk/ui/settings/SettingsFragment.kt | 4 +- .../magisk/ui/surequest/SuRequestActivity.kt | 1 + .../magisk/utils/DynamicClassLoader.kt | 61 ------- .../topjohnwu/magisk/utils/LocaleManager.kt | 68 ------- .../com/topjohnwu/magisk/utils/PatchAPK.kt | 2 +- .../com/topjohnwu/magisk/utils/ResourceMgr.kt | 126 +++++++++++++ .../com/topjohnwu/magisk/utils/RootInit.kt | 40 ++++ .../com/topjohnwu/magisk/utils/RootUtils.kt | 158 ---------------- .../java/com/topjohnwu/magisk/utils/Utils.kt | 5 + app/src/main/res/values-v19/styles.xml | 7 +- app/src/main/res/values/colors.xml | 4 - app/src/main/res/values/styles.xml | 4 +- build.gradle | 16 +- shared/src/main/AndroidManifest.xml | 6 + shared/src/main/java/a/r.java | 6 + .../com/topjohnwu/magisk/ProcessPhoenix.java | 90 +++++++++ .../magisk/utils/CompoundEnumeration.java | 35 ++++ .../com/topjohnwu/magisk/utils/DynAPK.java | 16 ++ .../magisk/utils/DynamicClassLoader.java | 60 ++++++ .../ic_splash_activity.xml | 11 ++ shared/src/main/res/values-az/strings.xml | 10 +- shared/src/main/res/values-bg/strings.xml | 2 + shared/src/main/res/values-ca/strings.xml | 2 + shared/src/main/res/values-de/strings.xml | 2 + shared/src/main/res/values-es/strings.xml | 2 + shared/src/main/res/values-et/strings.xml | 2 + shared/src/main/res/values-fr/strings.xml | 2 + shared/src/main/res/values-in/strings.xml | 2 + shared/src/main/res/values-it/strings.xml | 2 + shared/src/main/res/values-ko/strings.xml | 2 + shared/src/main/res/values-lt/strings.xml | 2 + shared/src/main/res/values-mk/strings.xml | 9 +- shared/src/main/res/values-nb/strings.xml | 2 + shared/src/main/res/values-pl/strings.xml | 2 + shared/src/main/res/values-ro/strings.xml | 2 + shared/src/main/res/values-ru/strings.xml | 2 + shared/src/main/res/values-sk/strings.xml | 2 + shared/src/main/res/values-tr/strings.xml | 2 + shared/src/main/res/values-uk/strings.xml | 2 + shared/src/main/res/values-v23/values.xml | 5 + shared/src/main/res/values-zh-rCN/strings.xml | 2 + shared/src/main/res/values-zh-rTW/strings.xml | 2 + shared/src/main/res/values/colors.xml | 4 + shared/src/main/res/values/strings.xml | 3 +- shared/src/main/res/values/styles.xml | 14 ++ shared/src/main/res/values/values.xml | 6 + stub/build.gradle | 5 +- stub/src/main/AndroidManifest.xml | 171 +++++++++++++++++- stub/src/main/java/a/a.java | 6 + stub/src/main/java/a/c.java | 6 + stub/src/main/java/a/e.java | 6 + .../work/impl/WorkManagerInitializer.java | 12 ++ .../topjohnwu/magisk/DelegateApplication.java | 72 ++++++++ .../magisk/DelegateComponentFactory.java | 72 ++++++++ ...ainActivity.java => DownloadActivity.java} | 46 +++-- .../topjohnwu/magisk/dummy/DummyActivity.java | 13 ++ .../topjohnwu/magisk/dummy/DummyProvider.java | 38 ++++ .../topjohnwu/magisk/dummy/DummyReceiver.java | 10 + .../topjohnwu/magisk/dummy/DummyService.java | 13 ++ stub/src/main/res/values-az/strings.xml | 4 - stub/src/main/res/values-bg/strings.xml | 5 - stub/src/main/res/values-ca/strings.xml | 4 - stub/src/main/res/values-de/strings.xml | 5 - stub/src/main/res/values-es/strings.xml | 5 - stub/src/main/res/values-et/strings.xml | 4 - stub/src/main/res/values-fr/strings.xml | 5 - stub/src/main/res/values-in/strings.xml | 5 - stub/src/main/res/values-it/strings.xml | 5 - stub/src/main/res/values-ko/strings.xml | 4 - stub/src/main/res/values-lt/strings.xml | 5 - stub/src/main/res/values-mk/strings.xml | 4 - stub/src/main/res/values-nb/strings.xml | 5 - stub/src/main/res/values-pl/strings.xml | 5 - stub/src/main/res/values-ro/strings.xml | 5 - stub/src/main/res/values-ru/strings.xml | 5 - stub/src/main/res/values-sk/strings.xml | 4 - stub/src/main/res/values-tr/strings.xml | 5 - stub/src/main/res/values-uk/strings.xml | 4 - stub/src/main/res/values-v28/styles.xml | 4 + stub/src/main/res/values-zh-rCN/strings.xml | 5 - stub/src/main/res/values-zh-rTW/strings.xml | 5 - stub/src/main/res/values/strings.xml | 4 - 98 files changed, 1194 insertions(+), 492 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/base/BaseReceiver.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/base/BaseService.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/LocaleManager.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/ResourceMgr.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt create mode 100644 shared/src/main/java/a/r.java create mode 100644 shared/src/main/java/com/topjohnwu/magisk/ProcessPhoenix.java create mode 100644 shared/src/main/java/com/topjohnwu/magisk/utils/CompoundEnumeration.java create mode 100644 shared/src/main/java/com/topjohnwu/magisk/utils/DynAPK.java create mode 100644 shared/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.java create mode 100644 shared/src/main/res/drawable-anydpi-v23/ic_splash_activity.xml create mode 100644 shared/src/main/res/values-v23/values.xml create mode 100644 shared/src/main/res/values/colors.xml create mode 100644 shared/src/main/res/values/styles.xml create mode 100644 shared/src/main/res/values/values.xml create mode 100644 stub/src/main/java/a/a.java create mode 100644 stub/src/main/java/a/c.java create mode 100644 stub/src/main/java/a/e.java create mode 100644 stub/src/main/java/androidx/work/impl/WorkManagerInitializer.java create mode 100644 stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java create mode 100644 stub/src/main/java/com/topjohnwu/magisk/DelegateComponentFactory.java rename stub/src/main/java/com/topjohnwu/magisk/{MainActivity.java => DownloadActivity.java} (63%) create mode 100644 stub/src/main/java/com/topjohnwu/magisk/dummy/DummyActivity.java create mode 100644 stub/src/main/java/com/topjohnwu/magisk/dummy/DummyProvider.java create mode 100644 stub/src/main/java/com/topjohnwu/magisk/dummy/DummyReceiver.java create mode 100644 stub/src/main/java/com/topjohnwu/magisk/dummy/DummyService.java delete mode 100644 stub/src/main/res/values-az/strings.xml delete mode 100644 stub/src/main/res/values-bg/strings.xml delete mode 100644 stub/src/main/res/values-ca/strings.xml delete mode 100644 stub/src/main/res/values-de/strings.xml delete mode 100644 stub/src/main/res/values-es/strings.xml delete mode 100644 stub/src/main/res/values-et/strings.xml delete mode 100644 stub/src/main/res/values-fr/strings.xml delete mode 100644 stub/src/main/res/values-in/strings.xml delete mode 100644 stub/src/main/res/values-it/strings.xml delete mode 100644 stub/src/main/res/values-ko/strings.xml delete mode 100644 stub/src/main/res/values-lt/strings.xml delete mode 100644 stub/src/main/res/values-mk/strings.xml delete mode 100644 stub/src/main/res/values-nb/strings.xml delete mode 100644 stub/src/main/res/values-pl/strings.xml delete mode 100644 stub/src/main/res/values-ro/strings.xml delete mode 100644 stub/src/main/res/values-ru/strings.xml delete mode 100644 stub/src/main/res/values-sk/strings.xml delete mode 100644 stub/src/main/res/values-tr/strings.xml delete mode 100644 stub/src/main/res/values-uk/strings.xml create mode 100644 stub/src/main/res/values-v28/styles.xml delete mode 100644 stub/src/main/res/values-zh-rCN/strings.xml delete mode 100644 stub/src/main/res/values-zh-rTW/strings.xml delete mode 100644 stub/src/main/res/values/strings.xml diff --git a/README.MD b/README.MD index 83d542432..2a5be0cb5 100644 --- a/README.MD +++ b/README.MD @@ -33,7 +33,6 @@ Furthermore, Magisk provides a **Systemless Interface** to alter the system (or Default string resources for Magisk Manager are scattered throughout - `app/src/main/res/values/strings.xml` -- `stub/src/main/res/values/strings.xml` - `shared/src/main/res/values/strings.xml` Translate each and place them in the respective locations (`/src/main/res/values-/strings.xml`). diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 163ef7469..4d76d7753 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,4 +1,22 @@ + + + @@ -11,32 +29,37 @@ + tools:ignore="UnusedAttribute,GoogleAppIndexingWarning" + tools:replace="android:appComponentFactory"> - + - + + + + + + + + android:screenOrientation="nosensor" /> @@ -44,8 +67,7 @@ android:name="a.m" android:directBootAware="true" android:excludeFromRecents="true" - android:exported="false" - android:theme="@style/MagiskTheme.SU" /> + android:exported="false" /> @@ -64,9 +86,10 @@ - + - diff --git a/app/src/main/java/a/a.java b/app/src/main/java/a/a.java index ec5a4e698..5ccb6738b 100644 --- a/app/src/main/java/a/a.java +++ b/app/src/main/java/a/a.java @@ -1,13 +1,19 @@ package a; +import androidx.annotation.Keep; +import androidx.core.app.AppComponentFactory; + import com.topjohnwu.magisk.utils.PatchAPK; import com.topjohnwu.signing.BootSigner; -import androidx.annotation.Keep; - @Keep -public class a extends BootSigner { +public class a extends AppComponentFactory { + public static boolean patchAPK(String in, String out, String pkg) { return PatchAPK.patch(in, out, pkg); } + + public static void main(String[] args) throws Exception { + BootSigner.main(args); + } } diff --git a/app/src/main/java/a/w.java b/app/src/main/java/a/w.java index f852a8968..6fa3e50f6 100644 --- a/app/src/main/java/a/w.java +++ b/app/src/main/java/a/w.java @@ -7,6 +7,7 @@ import androidx.work.Worker; import androidx.work.WorkerParameters; import com.topjohnwu.magisk.base.DelegateWorker; +import com.topjohnwu.magisk.utils.ResourceMgrKt; import java.lang.reflect.ParameterizedType; @@ -18,7 +19,7 @@ public abstract class w extends Worker { @SuppressWarnings("unchecked") w(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); + super(ResourceMgrKt.wrap(context, false), workerParams); try { base = ((Class) ((ParameterizedType) getClass().getGenericSuperclass()) .getActualTypeArguments()[0]).newInstance(); diff --git a/app/src/main/java/com/topjohnwu/magisk/App.kt b/app/src/main/java/com/topjohnwu/magisk/App.kt index b742d5175..3a3f6961c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/App.kt +++ b/app/src/main/java/com/topjohnwu/magisk/App.kt @@ -13,8 +13,11 @@ import com.topjohnwu.magisk.data.database.RepoDatabase_Impl import com.topjohnwu.magisk.di.ActivityTracker import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.magisk.extensions.get -import com.topjohnwu.magisk.utils.LocaleManager -import com.topjohnwu.magisk.utils.RootUtils +import com.topjohnwu.magisk.extensions.unwrap +import com.topjohnwu.magisk.utils.ResourceMgr +import com.topjohnwu.magisk.utils.RootInit +import com.topjohnwu.magisk.utils.isRunningAsStub +import com.topjohnwu.magisk.utils.wrap import com.topjohnwu.superuser.Shell import org.koin.android.ext.koin.androidContext import org.koin.core.context.startKoin @@ -26,7 +29,7 @@ open class App : Application() { AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) Shell.Config.setFlags(Shell.FLAG_MOUNT_MASTER or Shell.FLAG_USE_MAGISK_BUSYBOX) Shell.Config.verboseLogging(BuildConfig.DEBUG) - Shell.Config.addInitializers(RootUtils::class.java) + Shell.Config.addInitializers(RootInit::class.java) Shell.Config.setTimeout(2) Room.setFactory { when (it) { @@ -38,22 +41,42 @@ open class App : Application() { } override fun attachBaseContext(base: Context) { - super.attachBaseContext(base) + // Basic setup if (BuildConfig.DEBUG) MultiDex.install(base) Timber.plant(Timber.DebugTree()) + // Some context magic + val app: Application + val impl: Context + if (base is Application) { + isRunningAsStub = true + app = base + impl = base.baseContext + } else { + app = this + impl = base + } + ResourceMgr.init(impl) + super.attachBaseContext(impl.wrap()) + + // Normal startup startKoin { - androidContext(this@App) + androidContext(baseContext) modules(koinModules) } + ResourceMgr.reload() + app.registerActivityLifecycleCallbacks(get()) + } - registerActivityLifecycleCallbacks(get()) - LocaleManager.setLocale(this) + // This is required as some platforms expect ContextImpl + override fun getBaseContext(): Context { + return super.getBaseContext().unwrap() } override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - LocaleManager.setLocale(this) + ResourceMgr.reload(newConfig) + if (!isRunningAsStub) + super.onConfigurationChanged(newConfig) } } diff --git a/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt b/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt index 04dad084f..0b8fed26f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/BaseActivity.kt @@ -15,12 +15,13 @@ import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.Config +import com.topjohnwu.magisk.R import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.extensions.set import com.topjohnwu.magisk.model.events.EventHandler import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder -import com.topjohnwu.magisk.utils.LocaleManager import com.topjohnwu.magisk.utils.currentLocale +import com.topjohnwu.magisk.utils.wrap import kotlin.random.Random typealias RequestCallback = BaseActivity<*, *>.(Int, Intent?) -> Unit @@ -31,9 +32,8 @@ abstract class BaseActivity() } @@ -53,10 +53,11 @@ abstract class BaseActivity().packageName @@ -93,6 +97,105 @@ fun Context.readUri(uri: Uri) = fun Intent.startActivity(context: Context) = context.startActivity(this) +fun Intent.toCommand(args: MutableList) { + if (action != null) { + args.add("-a") + args.add(action!!) + } + if (component != null) { + args.add("-n") + args.add(component!!.flattenToString()) + } + if (data != null) { + args.add("-d") + args.add(dataString!!) + } + if (categories != null) { + for (cat in categories) { + args.add("-c") + args.add(cat) + } + } + if (type != null) { + args.add("-t") + args.add(type!!) + } + val extras = extras + if (extras != null) { + loop@ for (key in extras.keySet()) { + val v = extras.get(key) ?: continue + var value: Any = v + val arg: String + when { + v is String -> arg = "--es" + v is Boolean -> arg = "--ez" + v is Int -> arg = "--ei" + v is Long -> arg = "--el" + v is Float -> arg = "--ef" + v is Uri -> arg = "--eu" + v is ComponentName -> { + arg = "--ecn" + value = v.flattenToString() + } + v is ArrayList<*> -> { + if (v.size <= 0) + /* Impossible to know the type due to type erasure */ + continue@loop + + arg = if (v[0] is Int) + "--eial" + else if (v[0] is Long) + "--elal" + else if (v[0] is Float) + "--efal" + else if (v[0] is String) + "--esal" + else + continue@loop /* Unsupported */ + + val sb = StringBuilder() + for (o in v) { + sb.append(o.toString().replace(",", "\\,")) + sb.append(',') + } + // Remove trailing comma + sb.deleteCharAt(sb.length - 1) + value = sb + } + v.javaClass.isArray -> { + arg = if (v is IntArray) + "--eia" + else if (v is LongArray) + "--ela" + else if (v is FloatArray) + "--efa" + else if (v is Array<*> && v.isArrayOf()) + "--esa" + else + continue@loop /* Unsupported */ + + val sb = StringBuilder() + val len = java.lang.reflect.Array.getLength(v) + for (i in 0 until len) { + sb.append(java.lang.reflect.Array.get(v, i)!!.toString().replace(",", "\\,")) + sb.append(',') + } + // Remove trailing comma + sb.deleteCharAt(sb.length - 1) + value = sb + } + else -> continue@loop + } /* Unsupported */ + + args.add(arg) + args.add(key) + args.add(value.toString()) + } + } + args.add("-f") + args.add(flags.toString()) +} + fun File.provide(context: Context = get()): Uri { return FileProvider.getUriForFile(context, context.packageName + ".provider", this) } @@ -157,3 +260,18 @@ fun Context.startEndToLeftRight(start: Int, end: Int): Pair { } fun Context.openUrl(url: String) = Utils.openLink(this, url.toUri()) + +@Suppress("FunctionName") +inline fun T.DynamicClassLoader(apk: File) + = DynamicClassLoader(apk, T::class.java.classLoader) + +fun Context.unwrap() : Context { + var context = this + while (true) { + if (context is ContextWrapper) + context = context.baseContext + else + break + } + return context +} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt index 4fdf3b9fe..278edafb0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt @@ -5,13 +5,13 @@ import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.extensions.DynamicClassLoader import com.topjohnwu.magisk.model.entity.internal.Configuration.APK.Restore import com.topjohnwu.magisk.model.entity.internal.Configuration.APK.Upgrade import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.ui.SplashActivity -import com.topjohnwu.magisk.utils.DynamicClassLoader import com.topjohnwu.magisk.utils.PatchAPK -import com.topjohnwu.magisk.utils.RootUtils +import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.superuser.Shell import timber.log.Timber import java.io.File @@ -54,7 +54,7 @@ private fun RemoteFileService.restore(apk: File, id: Int) { if (Shell.su("pm install $apk").exec().isSuccess) { val component = ComponentName(BuildConfig.APPLICATION_ID, ClassMap.get>(SplashActivity::class.java).name) - RootUtils.rmAndLaunch(packageName, component) + Utils.rmAndLaunch(packageName, component) } } diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/NotificationService.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/NotificationService.kt index 716869fc3..f558265e3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/NotificationService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/NotificationService.kt @@ -1,16 +1,16 @@ package com.topjohnwu.magisk.model.download import android.app.Notification -import android.app.Service import android.content.Intent import android.os.IBinder import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import com.topjohnwu.magisk.base.BaseService import org.koin.core.KoinComponent import java.util.* import kotlin.random.Random.Default.nextInt -abstract class NotificationService : Service(), KoinComponent { +abstract class NotificationService : BaseService(), KoinComponent { abstract val defaultNotification: NotificationCompat.Builder diff --git a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt index 93e277780..587712370 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt @@ -1,15 +1,14 @@ package com.topjohnwu.magisk.model.receiver -import android.content.BroadcastReceiver -import android.content.Context +import android.content.ContextWrapper import android.content.Intent import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Info +import com.topjohnwu.magisk.base.BaseReceiver import com.topjohnwu.magisk.data.database.PolicyDao import com.topjohnwu.magisk.data.database.base.su -import com.topjohnwu.magisk.extensions.inject import com.topjohnwu.magisk.extensions.reboot import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.entity.ManagerJson @@ -20,8 +19,9 @@ import com.topjohnwu.magisk.utils.SuLogger import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.superuser.Shell +import org.koin.core.inject -open class GeneralReceiver : BroadcastReceiver() { +open class GeneralReceiver : BaseReceiver() { private val policyDB: PolicyDao by inject() @@ -36,7 +36,7 @@ open class GeneralReceiver : BroadcastReceiver() { return intent.data?.encodedSchemeSpecificPart.orEmpty() } - override fun onReceive(context: Context, intent: Intent?) { + override fun onReceive(context: ContextWrapper, intent: Intent?) { intent ?: return when (intent.action ?: return) { Intent.ACTION_REBOOT, Intent.ACTION_BOOT_COMPLETED -> { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt index 8810447af..848192ca6 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt @@ -40,8 +40,8 @@ open class MainActivity : BaseActivity(), Na override val layoutRes: Int = R.layout.activity_main override val viewModel: MainViewModel by viewModel() - override val navHostId: Int = R.id.main_nav_host - override val defaultPosition: Int = 0 + private val navHostId: Int = R.id.main_nav_host + private val defaultPosition: Int = 0 private val navigationController by lazy { FragNavController(supportFragmentManager, navHostId) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt index 7021b62ba..728720b1b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt @@ -1,17 +1,23 @@ package com.topjohnwu.magisk.ui +import android.app.Activity +import android.content.Context import android.content.Intent import android.os.Bundle import android.text.TextUtils import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity import com.topjohnwu.magisk.* import com.topjohnwu.magisk.utils.Utils +import com.topjohnwu.magisk.utils.wrap import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.superuser.Shell -open class SplashActivity : AppCompatActivity() { +open class SplashActivity : Activity() { + + override fun attachBaseContext(base: Context) { + super.attachBaseContext(base.wrap()) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt index 2f46401a4..143db2a3e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt @@ -23,6 +23,7 @@ import java.io.File open class FlashActivity : BaseActivity() { override val layoutRes: Int = R.layout.activity_flash + override val themeRes: Int = R.style.MagiskTheme_Flashing override val viewModel: FlashViewModel by viewModel { val uri = intent.data ?: let { finish(); Uri.EMPTY } val additionalUri = intent.getParcelableExtra(Const.Key.FLASH_DATA) ?: uri diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt index 2cee4cbd8..2d2b2161c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt @@ -9,11 +9,11 @@ import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.databinding.FragmentMagiskBinding +import com.topjohnwu.magisk.extensions.DynamicClassLoader import com.topjohnwu.magisk.extensions.openUrl import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.writeTo import com.topjohnwu.magisk.model.events.* -import com.topjohnwu.magisk.utils.DynamicClassLoader import com.topjohnwu.magisk.utils.SafetyNetHelper import com.topjohnwu.magisk.view.MarkDownWindow import com.topjohnwu.magisk.view.dialogs.* diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt index 8f4d2fced..a3457ddd0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt @@ -201,7 +201,7 @@ class SettingsFragment : BasePreferenceFragment() { Shell.su("magiskhide --disable").submit() } Config.Key.LOCALE -> { - LocaleManager.setLocale(activity.application) + ResourceMgr.reload() activity.recreate() } Config.Key.CHECK_UPDATES -> Utils.scheduleUpdateCheck(activity) @@ -230,7 +230,7 @@ class SettingsFragment : BasePreferenceFragment() { val values = mutableListOf() names.add( - LocaleManager.getString(defaultLocale, R.string.system_default) + ResourceMgr.getString(defaultLocale, R.string.system_default) ) values.add("") diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt index 2fb93eeea..b55e108c2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt @@ -18,6 +18,7 @@ import org.koin.androidx.viewmodel.ext.android.viewModel open class SuRequestActivity : BaseActivity() { override val layoutRes: Int = R.layout.activity_request + override val themeRes: Int = R.style.MagiskTheme_SU override val viewModel: SuRequestViewModel by viewModel() override fun onBackPressed() { diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.kt b/app/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.kt deleted file mode 100644 index 9810c91bd..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.topjohnwu.magisk.utils - -import dalvik.system.DexClassLoader -import java.io.File -import java.io.IOException -import java.net.URL -import java.util.* - -@Suppress("FunctionName") -inline fun T.DynamicClassLoader(apk: File) = DynamicClassLoader(apk, T::class.java.classLoader) - -class DynamicClassLoader(apk: File, parent: ClassLoader?) - : DexClassLoader(apk.path, apk.parent, null, parent) { - - private val base by lazy { Any::class.java.classLoader!! } - - @Throws(ClassNotFoundException::class) - override fun loadClass(name: String, resolve: Boolean) : Class<*> - = findLoadedClass(name) ?: runCatching { - base.loadClass(name) - }.getOrElse { - runCatching { - findClass(name) - }.getOrElse { err -> - runCatching { - parent.loadClass(name) - }.getOrElse { throw err } - } - } - - override fun getResource(name: String) = base.getResource(name) - ?: findResource(name) - ?: parent?.getResource(name) - - @Throws(IOException::class) - override fun getResources(name: String): Enumeration { - val resources = mutableListOf( - base.getResources(name), - findResources(name), parent.getResources(name)) - return object : Enumeration { - override fun hasMoreElements(): Boolean { - while (true) { - if (resources.isEmpty()) - return false - if (!resources[0].hasMoreElements()) { - resources.removeAt(0) - } else { - return true - } - } - } - - override fun nextElement(): URL { - if (!hasMoreElements()) - throw NoSuchElementException() - return resources[0].nextElement() - } - } - } - -} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/LocaleManager.kt b/app/src/main/java/com/topjohnwu/magisk/utils/LocaleManager.kt deleted file mode 100644 index e89241b0d..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/LocaleManager.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.topjohnwu.magisk.utils - -import android.content.Context -import android.content.ContextWrapper -import android.content.res.Configuration -import android.content.res.Resources -import androidx.annotation.StringRes -import com.topjohnwu.magisk.Config -import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.extensions.get -import com.topjohnwu.magisk.extensions.inject -import com.topjohnwu.magisk.extensions.langTagToLocale -import com.topjohnwu.superuser.internal.InternalUtils -import io.reactivex.Single -import java.util.* - -var currentLocale = Locale.getDefault()!! - private set - -val defaultLocale = Locale.getDefault()!! - -val availableLocales = Single.fromCallable { - val compareId = R.string.app_changelog - val res: Resources by inject() - mutableListOf().apply { - // Add default locale - add(Locale.ENGLISH) - - // Add some special locales - add(Locale.TAIWAN) - add(Locale("pt", "BR")) - - // Other locales - val otherLocales = res.assets.locales - .map { it.langTagToLocale() } - .distinctBy { LocaleManager.getString(it, compareId) } - - listOf("", "").toTypedArray() - - addAll(otherLocales) - }.sortedWith(Comparator { a, b -> - a.getDisplayName(a).toLowerCase(a) - .compareTo(b.getDisplayName(b).toLowerCase(b)) - }) -}.cache()!! - -object LocaleManager { - - fun setLocale(wrapper: ContextWrapper) { - val localeConfig = Config.locale - currentLocale = when { - localeConfig.isEmpty() -> defaultLocale - else -> localeConfig.langTagToLocale() - } - Locale.setDefault(currentLocale) - InternalUtils.replaceBaseContext(wrapper, getLocaleContext(wrapper, currentLocale)) - } - - fun getLocaleContext(context: Context, locale: Locale = currentLocale): Context { - val config = Configuration(context.resources.configuration) - config.setLocale(locale) - return context.createConfigurationContext(config) - } - - fun getString(locale: Locale, @StringRes id: Int): String { - return getLocaleContext(get(), locale).getString(id) - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt index 5f45dc8a7..1ed022ead 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt @@ -102,7 +102,7 @@ object PatchAPK { Config.suManager = pkg Config.export() - RootUtils.rmAndLaunch(BuildConfig.APPLICATION_ID, + Utils.rmAndLaunch(BuildConfig.APPLICATION_ID, ComponentName(pkg, ClassMap.get>(SplashActivity::class.java).name)) return true diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/ResourceMgr.kt b/app/src/main/java/com/topjohnwu/magisk/utils/ResourceMgr.kt new file mode 100644 index 000000000..fb3d7d503 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/ResourceMgr.kt @@ -0,0 +1,126 @@ +@file:Suppress("DEPRECATION") + +package com.topjohnwu.magisk.utils + +import android.annotation.SuppressLint +import android.content.Context +import android.content.ContextWrapper +import android.content.res.AssetManager +import android.content.res.Configuration +import android.content.res.Resources +import androidx.annotation.StringRes +import com.topjohnwu.magisk.Config +import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.extensions.langTagToLocale +import io.reactivex.Single +import java.util.* + +var isRunningAsStub = false + +var currentLocale: Locale = Locale.getDefault() + private set + +@SuppressLint("ConstantLocale") +val defaultLocale: Locale = Locale.getDefault() + +val availableLocales = Single.fromCallable { + val compareId = R.string.app_changelog + mutableListOf().apply { + // Add default locale + add(Locale.ENGLISH) + + // Add some special locales + add(Locale.TAIWAN) + add(Locale("pt", "BR")) + + val config = Configuration() + val metrics = ResourceMgr.resource.displayMetrics + val res = Resources(ResourceMgr.resource.assets, metrics, config) + + // Other locales + val otherLocales = ResourceMgr.resource.assets.locales + .map { it.langTagToLocale() } + .distinctBy { + config.setLocale(it) + res.updateConfiguration(config, metrics) + res.getString(compareId) + } + + listOf("", "").toTypedArray() + + addAll(otherLocales) + }.sortedWith(Comparator { a, b -> + a.getDisplayName(a).toLowerCase(a) + .compareTo(b.getDisplayName(b).toLowerCase(b)) + }) +}.cache()!! + +private val addAssetPath by lazy { + AssetManager::class.java.getMethod("addAssetPath", String::class.java) +} + +fun AssetManager.addAssetPath(path: String) { + addAssetPath.invoke(this, path) +} + +fun Context.wrap(global: Boolean = true): Context + = if (!global) ResourceMgr.ResContext(this) else ResourceMgr.GlobalResContext(this) + +object ResourceMgr { + + lateinit var resource: Resources + private lateinit var resApk: String + + fun init(context: Context) { + resource = context.resources + if (isRunningAsStub) + resApk = DynAPK.current(context).path + } + + // Override locale and inject resources from dynamic APK + private fun Resources.patch(config: Configuration = Configuration(configuration)): Resources { + config.setLocale(currentLocale) + updateConfiguration(config, displayMetrics) + if (isRunningAsStub) + assets.addAssetPath(resApk) + return this + } + + fun reload(config: Configuration = Configuration(resource.configuration)) { + val localeConfig = Config.locale + currentLocale = when { + localeConfig.isEmpty() -> defaultLocale + else -> localeConfig.langTagToLocale() + } + Locale.setDefault(currentLocale) + resource.patch(config) + } + + fun getString(locale: Locale, @StringRes id: Int): String { + val config = Configuration() + config.setLocale(locale) + return Resources(resource.assets, resource.displayMetrics, config).getString(id) + } + + open class GlobalResContext(base: Context) : ContextWrapper(base) { + open val mRes: Resources get() = resource + private val loader by lazy { javaClass.classLoader!! } + + override fun getResources(): Resources { + return mRes + } + + override fun getClassLoader(): ClassLoader { + return loader + } + + override fun createConfigurationContext(config: Configuration): Context { + return ResContext(super.createConfigurationContext(config)) + } + } + + class ResContext(base: Context) : GlobalResContext(base) { + override val mRes by lazy { base.resources.patch() } + } + +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt b/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt new file mode 100644 index 000000000..c4bbfde4e --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt @@ -0,0 +1,40 @@ +package com.topjohnwu.magisk.utils + +import android.content.Context +import com.topjohnwu.magisk.Const +import com.topjohnwu.magisk.Info +import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.extensions.rawResource +import com.topjohnwu.superuser.Shell +import com.topjohnwu.superuser.ShellUtils +import com.topjohnwu.superuser.io.SuFile + +class RootInit : Shell.Initializer() { + + override fun onInit(context: Context, shell: Shell): Boolean { + return init(context.wrap(), shell) + } + + fun init(context: Context, shell: Shell): Boolean { + val job = shell.newJob() + if (shell.isRoot) { + job.add(context.rawResource(R.raw.util_functions)) + .add(context.rawResource(R.raw.utils)) + Const.MAGISK_DISABLE_FILE = SuFile("/cache/.disable_magisk") + Info.loadMagiskInfo() + } else { + job.add(context.rawResource(R.raw.nonroot_utils)) + } + + job.add("mount_partitions", + "get_flags", + "run_migrations", + "export BOOTMODE=true") + .exec() + + Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean() + Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean() + Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean() + return true + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt b/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt deleted file mode 100644 index 11bd46695..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt +++ /dev/null @@ -1,158 +0,0 @@ -package com.topjohnwu.magisk.utils - -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.net.Uri -import com.topjohnwu.magisk.Const -import com.topjohnwu.magisk.Info -import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.extensions.rawResource -import com.topjohnwu.magisk.extensions.toShellCmd -import com.topjohnwu.superuser.Shell -import com.topjohnwu.superuser.ShellUtils -import com.topjohnwu.superuser.io.SuFile -import java.util.* -import java.lang.reflect.Array as RArray - -fun Intent.toCommand(args: MutableList) { - if (action != null) { - args.add("-a") - args.add(action!!) - } - if (component != null) { - args.add("-n") - args.add(component!!.flattenToString()) - } - if (data != null) { - args.add("-d") - args.add(dataString!!) - } - if (categories != null) { - for (cat in categories) { - args.add("-c") - args.add(cat) - } - } - if (type != null) { - args.add("-t") - args.add(type!!) - } - val extras = extras - if (extras != null) { - loop@ for (key in extras.keySet()) { - val v = extras.get(key) ?: continue - var value: Any = v - val arg: String - when { - v is String -> arg = "--es" - v is Boolean -> arg = "--ez" - v is Int -> arg = "--ei" - v is Long -> arg = "--el" - v is Float -> arg = "--ef" - v is Uri -> arg = "--eu" - v is ComponentName -> { - arg = "--ecn" - value = v.flattenToString() - } - v is ArrayList<*> -> { - if (v.size <= 0) - /* Impossible to know the type due to type erasure */ - continue@loop - - arg = if (v[0] is Int) - "--eial" - else if (v[0] is Long) - "--elal" - else if (v[0] is Float) - "--efal" - else if (v[0] is String) - "--esal" - else - continue@loop /* Unsupported */ - - val sb = StringBuilder() - for (o in v) { - sb.append(o.toString().replace(",", "\\,")) - sb.append(',') - } - // Remove trailing comma - sb.deleteCharAt(sb.length - 1) - value = sb - } - v.javaClass.isArray -> { - arg = if (v is IntArray) - "--eia" - else if (v is LongArray) - "--ela" - else if (v is FloatArray) - "--efa" - else if (v is Array<*> && v.isArrayOf()) - "--esa" - else - continue@loop /* Unsupported */ - - val sb = StringBuilder() - val len = RArray.getLength(v) - for (i in 0 until len) { - sb.append(RArray.get(v, i)!!.toString().replace(",", "\\,")) - sb.append(',') - } - // Remove trailing comma - sb.deleteCharAt(sb.length - 1) - value = sb - } - else -> continue@loop - } /* Unsupported */ - - args.add(arg) - args.add(key) - args.add(value.toString()) - } - } - args.add("-f") - args.add(flags.toString()) -} - -fun startActivity(intent: Intent) { - if (intent.component == null) - return - val args = ArrayList() - args.add("am") - args.add("start") - intent.toCommand(args) - Shell.su(args.toShellCmd()).exec() -} - -class RootUtils : Shell.Initializer() { - - override fun onInit(context: Context, shell: Shell): Boolean { - val job = shell.newJob() - if (shell.isRoot) { - job.add(context.rawResource(R.raw.util_functions)) - .add(context.rawResource(R.raw.utils)) - Const.MAGISK_DISABLE_FILE = SuFile("/cache/.disable_magisk") - Info.loadMagiskInfo() - } else { - job.add(context.rawResource(R.raw.nonroot_utils)) - } - - job.add("mount_partitions", - "get_flags", - "run_migrations", - "export BOOTMODE=true") - .exec() - - Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean() - Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean() - Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean() - return true - } - - companion object { - - fun rmAndLaunch(rm: String, component: ComponentName) { - Shell.su("(rm_launch $rm ${component.flattenToString()})").exec() - } - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt index bf3758fe6..4e873aa8a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt @@ -1,5 +1,6 @@ package com.topjohnwu.magisk.utils +import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.res.Resources @@ -72,4 +73,8 @@ object Utils { if ((exists() && isDirectory) || mkdirs()) this else null } + fun rmAndLaunch(rm: String, component: ComponentName) { + Shell.su("(rm_launch $rm ${component.flattenToString()})").exec() + } + } diff --git a/app/src/main/res/values-v19/styles.xml b/app/src/main/res/values-v19/styles.xml index 785a6291e..2a7f9b3e4 100644 --- a/app/src/main/res/values-v19/styles.xml +++ b/app/src/main/res/values-v19/styles.xml @@ -1,9 +1,4 @@ - - + - + + + + +