diff --git a/.gitignore b/.gitignore index 6c6df16..e639d75 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,7 @@ logs/* config.json wallpaper.txt port.txt -ui_port.txt \ No newline at end of file +ui_port.txt +bin +.buildozer +test* \ No newline at end of file diff --git a/app_src/assets/icons/t.png b/app_src/assets/icons/both.png similarity index 100% rename from app_src/assets/icons/t.png rename to app_src/assets/icons/both.png diff --git a/app_src/hot_reload.py b/app_src/hot_reload.py index 1e131d2..1c12a5c 100644 --- a/app_src/hot_reload.py +++ b/app_src/hot_reload.py @@ -1,3 +1,4 @@ +# DEBUG=1 python3 hot_reload.py import os from kaki.app import App from kivy.factory import Factory @@ -74,7 +75,7 @@ class MDLive(App,MDApp): def build_app(self, *args): # return Factory.MyPopUp() - return Factory.GalleryScreen() + return Factory.FullscreenScreen() def on_start(self): # self.theme_cls.theme_style = "Light" diff --git a/app_src/ui/screens/full_screen.py b/app_src/ui/screens/full_screen.py index a8b359d..4e3dd52 100644 --- a/app_src/ui/screens/full_screen.py +++ b/app_src/ui/screens/full_screen.py @@ -4,8 +4,9 @@ from pathlib import Path from kivy.clock import Clock -from kivy.properties import ListProperty, ObjectProperty, NumericProperty +from kivy.properties import ListProperty, ObjectProperty, NumericProperty, DictProperty from kivy.uix.behaviors import ButtonBehavior +from kivy.uix.boxlayout import BoxLayout from kivy.uix.image import AsyncImage from kivy.uix.label import Label from kivy.metrics import dp, sp @@ -23,6 +24,9 @@ from utils.image_operations import thumbnail_path_for, get_image_info, share_image_to_other_app from utils.helper import appFolder, change_wallpaper from utils.config_manager import ConfigManager +from android_notify.config import on_android_platform +from android_notify.internal.java_classes import autoclass +from jnius import PythonJavaClass, java_method # from utils.constants import DEV from utils.model import get_app, GalleryTabs from kivy.loader import Loader @@ -32,6 +36,29 @@ my_config=ConfigManager() +if on_android_platform(): + View = autoclass("android.view.View") + + class _SystemUiRunnable(PythonJavaClass): + __javainterfaces__ = ['java/lang/Runnable'] + __javacontext__ = 'app' + + def __init__(self, flags): + super().__init__() + self.flags = flags + + @java_method('()V') + def run(self): + PythonActivity = autoclass("org.kivy.android.PythonActivity") + activity = PythonActivity.mActivity + decor = activity.getWindow().getDecorView() + decor.setSystemUiVisibility(self.flags) + + def _set_system_ui_visibility(flags): + activity = autoclass("org.kivy.android.PythonActivity").mActivity + activity.runOnUiThread(_SystemUiRunnable(flags)) + + class BorderMDBoxLayout(MDBoxLayout): line_width = NumericProperty(1) def __init__(self, **kwargs): @@ -72,12 +99,13 @@ def __init__(self, **kwargs): self.bg_color = 'black' self.theme_text_color = 'Custom' self.text_color = 'white' - + def on_disabled(self, instance, value) -> None: + self.opacity=0 if value else 1 class PictureButton(ButtonBehavior,MDRelativeLayout): app_src = ''#'/home/fabian/Documents/Laner/mobile/app_src/' - images = [app_src+"assets/icons/t.png",app_src+"assets/icons/moon.png",app_src+"assets/icons/sun.png"]#ListProperty([]) - # images = ["/home/fabian/Documents/Laner/mobile/app_src/assets/icons/t.png","/home/fabian/Documents/Laner/mobile/app_src/assets/icons/moon.png","/home/fabian/Documents/Laner/mobile/app_src/assets/icons/sun.png"]#ListProperty([]) + images = [app_src+"assets/icons/both.png",app_src+"assets/icons/moon.png",app_src+"assets/icons/sun.png"]#ListProperty([]) + # images = ["/home/fabian/Documents/Laner/mobile/app_src/assets/icons/both.png","/home/fabian/Documents/Laner/mobile/app_src/assets/icons/moon.png","/home/fabian/Documents/Laner/mobile/app_src/assets/icons/sun.png"]#ListProperty([]) img_sizes = [100,42,42] screen_color = ListProperty() fullscreen = ObjectProperty() @@ -109,30 +137,33 @@ def get_tab_from_index(self,index): return tab_name def on_release(self): - current_image = self.fullscreen.current_image + img_path = self.fullscreen.current_image gallery_screen = self.app.sm.gallery_screen old_tab = self.get_tab_from_index(self.i) + self.i = self.i + 1 if self.i < len(self.images) - 1 else 0 new_tab = self.get_tab_from_index(self.i) - self._update_image() - self.__change_tab_from_wallpaper_storage(current_image=current_image,old_tab=old_tab,new_tab=new_tab) + slide = self.fullscreen.remove_image_in_tab_carousel(img_src=img_path) + self.fullscreen.add_image_to_tab_carousel(slide,new_tab) + self.__change_tab_from_wallpaper_storage(current_image=img_path,old_tab=old_tab,new_tab=new_tab) # try: # gallery_screen.wallpapers.remove(current_image) # except ValueError as error_removing_path_from_wallpapers_list: # app_logger.error(f"error_removing_path_from_wallpapers_list: {error_removing_path_from_wallpapers_list}") try: - image_widget = gallery_screen.remove_wallpaper_from_thumbnails(wallpaper_path=current_image,tab=old_tab) + image_widget = gallery_screen.remove_wallpaper_from_thumbnails(wallpaper_path=img_path,tab=old_tab) except Exception as error_finding_widget: app_logger.error(f"Error Removing Widget: {error_finding_widget}") return None if not image_widget: - app_logger.error(f"Error finding PreviewImage Widget to remove and reuse for another, image_widget: {image_widget}") + app_logger.error(f"Error finding PreviewImage Widget to remove and reuse for another, img_path: {img_path} in tab:{old_tab}") return None gallery_screen.add_wallpaper_to_thumbnails(image_widget=image_widget,tab=new_tab) + # self.update_header_texts(img_src) return None @staticmethod @@ -168,7 +199,10 @@ def _update_image(self): class FullscreenScreen(MyMDScreen): - current_image: str # used in toggle btn + current_image=''# used in toggle btn + images_data= DictProperty({}) + tabs_carousel_widgets= DictProperty({}) + index=NumericProperty(-1) def __init__(self, **kwargs): super().__init__(**kwargs) @@ -227,12 +261,14 @@ def __init__(self, **kwargs): self.header_title.text_color = 'white' self.header_file_size.text_color = [.6,.6,.6,1] - self.share_btn = MyMDIconButton(icon="share", style="tonal",on_release=lambda x: share_image_to_other_app(self.current_image)) + self.share_btn = MyMDIconButton(icon="share", style="tonal",on_release=lambda x: share_image_to_other_app(self.current_image),theme_text_color="Custom",disabled_color=[0,0,0,0]) + self.original_carousel_pos_hint = {'x': 0, 'y': 0.125} self.original_carousel_size_hint = (1, 1 - .25) - self.carousel = MyCarousel(direction="right", loop=True, - size_hint=self.original_carousel_size_hint, + # self.carousel=None + self.carousel_container=MDBoxLayout(size_hint=self.original_carousel_size_hint, pos_hint=self.original_carousel_pos_hint) + # self.carousel.bind(index=self.on_index) self.btm_btn_layout_root = MDRelativeLayout( @@ -283,7 +319,7 @@ def __init__(self, **kwargs): self.add_widget(self.layout) - self.layout.add_widget(self.carousel) + self.layout.add_widget(self.carousel_container) self.header_layout.add_widget(self.btn_toggle) self.text_container.add_widget(self.header_title) self.text_container.add_widget(self.header_file_size) @@ -309,19 +345,24 @@ def __init__(self, **kwargs): self.set_wallpaper_btn.bind(on_release=self.set_as_wallpaper) # print("using hot reload stuff") # self.update_images(0) # for hot_reload + self.load_images() def toggle_fullscreen(self, *_): # print(self.carousel.children[0].children) self.is_fullscreen = True + carousel = self.carousel_container.children[0] # carousel is only child - self.carousel.size_hint = (1, 1) - self.carousel.pos_hint = {'center_x': .5, 'center_y': .5} + self.carousel_container.size_hint = (1, 1) + self.carousel_container.pos_hint = {'center_x': .5, 'center_y': .5} + carousel.size_hint = (1, 1) + carousel.pos_hint = {'center_x': .5, 'center_y': .5} self.header_layout.md_bg_color = [0, 0, 0, 0] self.header_title.text_color = [0, 0, 0, 0] self.header_layout.bg_color_instr.a = 0 self.header_file_size.text_color = [0, 0, 0, 0] self.header_file_size.md_bg_color = [0, 0, 0, 0] + self.share_btn.disabled=1 self.btn_toggle.text_color = [1, 1, 1, .9] self.btn_toggle.style = "outlined" @@ -329,21 +370,33 @@ def toggle_fullscreen(self, *_): # self.btn_layout.disabled = True self.btn_toggle.icon = "close" - for img in self.carousel.slides: + for img in carousel.slides: img.fit_mode = "cover" self.layout.do_layout() + if on_android_platform(): + _set_system_ui_visibility( + View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + ) + def toggle_top_button(self, *_): # If in fullscreen mode → restore controls + carousel = self.carousel_container.children[0] # carousel is only child + if self.btn_toggle.icon == "close": - self.carousel.size_hint = self.original_carousel_size_hint - self.carousel.pos_hint = self.original_carousel_pos_hint + self.carousel_container.size_hint = self.original_carousel_size_hint + self.carousel_container.pos_hint = self.original_carousel_pos_hint + carousel.size_hint = self.original_carousel_size_hint + carousel.pos_hint = self.original_carousel_pos_hint self.header_layout.pos_hint = {'center_x': .5, 'top': .97} self.header_title.text_color = [1, 1, 1, 1] self.header_layout.bg_color_instr.a = .8 self.header_file_size.md_bg_color = [1, 1, 1, .2] self.header_file_size.text_color = [.6, .6, .6, 1] + self.share_btn.disabled = 0 self.btm_btn_layout_root.pos_hint = {"y": 0} @@ -354,7 +407,10 @@ def toggle_top_button(self, *_): self.btn_toggle.text_color = [1, 1, 1, 1] self.is_fullscreen = False - for img in self.carousel.slides: + if on_android_platform(): + _set_system_ui_visibility(0) + + for img in carousel.slides: img.fit_mode = "contain" # If not fullscreen → go back to thumbnails screen @@ -366,7 +422,8 @@ def set_as_wallpaper(self, *args): spinner_layout = LoadingLayout() def remove_spinner(dt): spinner_layout.remove() - threading.Thread(target=change_wallpaper, args=[self.carousel.current_slide.higher_format, remove_spinner]).start() + carousel = self.carousel_container.children[0] # carousel is only child + threading.Thread(target=change_wallpaper, args=[carousel.current_slide.source, remove_spinner]).start() def delete_current(self, *_): spinner_layout = LoadingLayout() @@ -376,8 +433,10 @@ def delete_current(self, *_): if not wallpapers: spinner_layout.remove() return + + carousel= self.carousel_container.children[0]# carousel is only child - idx = self.carousel.index + idx = carousel.index # Get path without removing it from the list directly path = wallpapers[idx] @@ -406,21 +465,23 @@ def delete_current(self, *_): spinner_layout.remove() return - self.update_images() + # self.update_images() + self.remove_image_in_tab_carousel(img_src=path) new_index=max(0, min(idx, len(gallery_screen.wallpapers) - 1)) - self.carousel.index = new_index - self.__patch_for_first_not_getting_called_by_on_current_slide(index=new_index) + carousel.index = new_index + # self.__patch_for_first_not_getting_called_by_on_current_slide(index=new_index) spinner_layout.remove() # ==================================================================== # IMAGE INFO POPUP # ==================================================================== def show_info(self, *_): + carousel = self.carousel_container.children[0] # carousel is only child gallery_screen = self.manager.gallery_screen if not gallery_screen.wallpapers: return - idx = self.carousel.index + idx = carousel.index path = gallery_screen.wallpapers[idx] popup = MyPopUp( @@ -431,36 +492,34 @@ def show_info(self, *_): ) popup.open() - def update_images(self,index=None): + # def update_images(self,index=None): + def load_images(self,index=None): """Rebuild carousel anytime wallpapers change.""" - self.carousel.unbind(current_slide=self.on_current_slide) self.carousel_has_images = False - gallery_screen = self.manager.gallery_screen - self.carousel.clear_widgets() + tabs = [GalleryTabs.BOTH.value, GalleryTabs.DAY.value, GalleryTabs.NOON.value ] + data = self.images_data + self.tabs_carousel_widgets={} # for hot_reload - # self.data = ["/home/fabian/Pictures/test.jpg"] - # for p in self.data: - for p in gallery_screen.wallpapers: - # print("thumbnail_path_for(p)", str(thumbnail_path_for(p))) - img = AsyncImage( - source=str(thumbnail_path_for(p)), # p, - # allow_stretch=True, - # keep_ratio=True, - fit_mode="contain", - # on_load=self.set_side_by_side1 - ) - img.higher_format = p - self.carousel_has_images = True - self.carousel.add_widget(img) - self.carousel.bind(current_slide=self.on_current_slide) + # data = {"Both":["/home/fabian/Pictures/test.jpg"]} + # for p in data: + # each_tab="Both" - # Setting data when entering Carousel From first slide because self.carousel.bind(current_slide=self.on_current_slide) isn't called - self.__patch_for_first_not_getting_called_by_on_current_slide(index) - - def __patch_for_first_not_getting_called_by_on_current_slide(self,index): - if index == 0: - self.on_current_slide(self.carousel,0) + for each_tab in tabs: + wallpapers=data[each_tab] + carousel = MyCarousel(direction="right", loop=True, + size_hint=self.original_carousel_size_hint, + pos_hint=self.original_carousel_pos_hint) + for p in wallpapers: + img = AsyncImage( + source=p, + fit_mode="contain", + ) + img.higher_format = p + carousel.add_widget(img) + # carousel.bind(current_slide=self.on_current_slide) + self.carousel_has_images = True + self.tabs_carousel_widgets[each_tab] = carousel def update_header_texts(self,image_path): self.header_title.text = os.path.basename(image_path) @@ -478,65 +537,58 @@ def update_header_texts(self,image_path): self.day_noon_both_button.set_day_nd_noon_image() def on_current_slide(self, carousel, index): # type: ignore - """Using on_current_slide instead of on_index to prevent multiple Calls""" - # print("self.carousel_has_images",self.carousel_has_images) - if not self.carousel_has_images or not carousel.current_slide: # From self.carousel.clear_widgets() changes current_slide Carousel.clear_widgets.remove_widget - return None - current_slide = carousel.current_slide - - if hasattr(self.carousel.current_slide,"higher_format"): - self.current_image = self.carousel.current_slide.higher_format - # print('there',self.carousel.current_slide.higher_format, current_slide.higher_format, self.clock_for_side_by_side,self.clock_for_higher_format) - - if self.clock_for_side_by_side: - self.clock_for_side_by_side.cancel() - self.clock_for_side_by_side = None - if self.clock_for_higher_format: - self.clock_for_higher_format.cancel() - self.clock_for_higher_format = None - - def change_img(_): - current_slide.source = str(current_slide.higher_format) - - self.update_header_texts(current_slide.higher_format) - self.clock_for_higher_format = Clock.schedule_once(change_img, 1) - self.clock_for_side_by_side = Clock.schedule_once(self.set_side_by_side, 1.5) - return None - - def set_side_by_side(self, *_): - """ - Set High res img for left and right side. - """ - # AsyncImage(on_load=self.set_side_by_side) not calling right - # print(str(self.carousel.current_slide.higher_format) != str(self.carousel.current_slide.source)) - - if str(self.carousel.current_slide.higher_format) != str(self.carousel.current_slide.source): - # Not setting for high format image, so return + if not current_slide: # this method gets called when using remove_widget to remove slide + print("no slide") + self.manager.go_to_thumbs() return None + img_src=current_slide.higher_format + self.current_image = img_src + # print("self.current_image",self.current_image) + self.update_header_texts(img_src) + print() + return None - current_slide_index = self.carousel.index - first_img = self.carousel.slides[0] - last_img = self.carousel.slides[-1] - - left_side_img = self.carousel.slides[current_slide_index - 1] if current_slide_index - 1 >= 0 else last_img - right_side_img = self.carousel.slides[current_slide_index + 1] if current_slide_index + 1 < len( - self.carousel.slides) else first_img - - # print("left_side_img source:", os.path.basename(left_side_img.source), "left_side_img hf:", - # os.path.basename(left_side_img.higher_format)) - if left_side_img and left_side_img.source != str(left_side_img.higher_format): - # print('left...') - proxyImage = Loader.image(str(left_side_img.higher_format)) - proxyImage.bind(on_load= lambda proxy_image, image_object=left_side_img: patch_resolution(proxy_image,image_object)) - # left_side_img.source = str(left_side_img.higher_format) - - if right_side_img and right_side_img.source != str(right_side_img.higher_format): - proxyImage = Loader.image(str(right_side_img.higher_format)) - proxyImage.bind(on_load=lambda proxy_image, image_object=right_side_img: patch_resolution(proxy_image, image_object)) - # right_side_img.source = str(right_side_img.higher_format) + def on_leave(self, *args): + self.index=-1 + carousel = self.carousel_container.children[0] + carousel.unbind(current_slide=self.on_current_slide) + + def on_index(self,widget,value): + current_tab=self.manager.gallery_screen.current_tab + self.carousel_container.clear_widgets() + self.carousel_container.add_widget(self.tabs_carousel_widgets[current_tab]) + carousel = self.tabs_carousel_widgets[current_tab] + carousel.bind(current_slide=self.on_current_slide) + if carousel.slides: + carousel.index = value + if value == 0: + self.on_current_slide(carousel,0) + + def remove_image_in_tab_carousel(self,img_src,tab_name=None): # tab_name args for when i add multiselect feature to gallery screen + tab_name=tab_name or self.manager.gallery_screen.current_tab + carousel = self.tabs_carousel_widgets[tab_name] # same location in memory with current carousel if displaying so no need for calling self.carousel_container.children[0] + index=0 + slides=carousel.slides + for slide in slides: + if slide.source==img_src: + # print("found:", os.path.basename(img_src)) + carousel.remove_widget(slide) + return slide + index+=1 + + app_logger.error(f"slide not found for path: {img_src}, tab_name: {tab_name}") + # if slides and index+1 < len(slides): # if not empty + # self.update_header_texts(img_src) return None + def add_image_to_tab_carousel(self,image_widget,tab_name): + carousel = self.tabs_carousel_widgets[tab_name] + print('adding...',image_widget,tab_name,carousel.children,carousel.slides) + if carousel._index is None: + carousel._index = 0 + carousel.add_widget(image_widget,len(carousel.slides)) + def patch_resolution(proxy_image, image_object): - image_object.texture = proxy_image.image.texture \ No newline at end of file + image_object.texture = proxy_image.image.texture diff --git a/app_src/ui/screens/gallery_screen.py b/app_src/ui/screens/gallery_screen.py index 1592854..06ad341 100644 --- a/app_src/ui/screens/gallery_screen.py +++ b/app_src/ui/screens/gallery_screen.py @@ -6,7 +6,7 @@ from kivy.clock import Clock from kivy.core.window import Window from kivy.metrics import dp, sp -from kivy.properties import StringProperty, NumericProperty, ListProperty, BooleanProperty, ObjectProperty +from kivy.properties import StringProperty, NumericProperty, ListProperty, BooleanProperty, ObjectProperty, DictProperty from kivy.uix.behaviors import ButtonBehavior from kivy.uix.button import Button from kivy.uix.image import AsyncImage @@ -298,6 +298,7 @@ def change_preview_img_size(self,widget,number_of_cols): class GalleryScreen(MyMDScreen): current_tab = StringProperty(GalleryTabs.BOTH.value) wallpapers = ListProperty([]) + images_data = DictProperty({}) def __init__(self, **kwargs): @@ -347,6 +348,11 @@ def run_widgets_creation( *args): self.generate_tab_widgets(tab_name=GalleryTabs.NOON.value, wallpapers=self._filter_existing_paths(my_config.get_noon_wallpapers())) self.current_tab = GalleryTabs.BOTH.value self.on_current_tab(None, self.current_tab) + self.images_data={ #for full_screen init + GalleryTabs.BOTH.value: self.tab_instances[GalleryTabs.BOTH.value]["wallpapers"], + GalleryTabs.DAY.value: self.tab_instances[GalleryTabs.DAY.value]["wallpapers"], + GalleryTabs.NOON.value: self.tab_instances[GalleryTabs.NOON.value]["wallpapers"], + } if no_clock: # using clock in init breaks DateGroupLayout height run_widgets_creation() @@ -409,11 +415,12 @@ def show_chooser(dt=None): # print("error_testing_picker", error_testing_picker) def generate_tab_widgets(self, tab_name, wallpapers, dt=None): - self.wallpapers = wallpapers + # self.wallpapers = wallpapers tab_title = f"{len(self.wallpapers)} images found" self.wallpapers = sorted( - self.wallpapers, + wallpapers, + # self.wallpapers, key=lambda image_path: os.stat(image_path).st_mtime, reverse=True # newest first ) diff --git a/app_src/ui/screens/logs_screen.py b/app_src/ui/screens/logs_screen.py index 6bc74a1..d977d84 100644 --- a/app_src/ui/screens/logs_screen.py +++ b/app_src/ui/screens/logs_screen.py @@ -78,8 +78,8 @@ def __init__(self, **kwargs): # Buttons at bottom buttons = BoxLayout(size_hint_y=None, height=dp(50), spacing=dp(10)) - add_btn = Button(text="Add Test Log") - add_btn.bind(on_release=lambda *_: self.add_test_log()) + add_btn = Button(text="Export Logs") + add_btn.bind(on_release=lambda *_: self.export_logs()) clear_btn = Button(text="Clear Logs") clear_btn.bind(on_release=lambda *_: self.clear_logs()) buttons.add_widget(add_btn) @@ -158,12 +158,68 @@ def add_log(self, message): f.write(full_log+"\n") self.scroll.scroll_y = 0 - def add_test_log(self): - self.add_log("INFO This is a normal log message") - self.add_log("WARN Something looks suspicious") - self.add_log("ERROR Something broke badly") - self.add_log("EXCEPTION ValueError: invalid input") - self.add_log("TRACEBACK File \"main.py\", line 42") + def export_logs(self, *_): + if not os.path.exists(self.log_file_path): + toast("No logs to export") + return + + from jnius import autoclass + from android_notify.config import get_python_activity_context + + context = get_python_activity_context() + + Environment = autoclass('android.os.Environment') + ContentValues = autoclass('android.content.ContentValues') + BuildVersion = autoclass('android.os.Build$VERSION') + File = autoclass('java.io.File') + FileInputStream = autoclass('java.io.FileInputStream') + + if BuildVersion.SDK_INT >= 29: + MediaStoreDownloads = autoclass('android.provider.MediaStore$Downloads') + MediaColumns = autoclass('android.provider.MediaStore$MediaColumns') + + content_values = ContentValues() + content_values.put(MediaColumns.DISPLAY_NAME, "app_logs.txt") + content_values.put(MediaColumns.MIME_TYPE, "text/plain") + content_values.put(MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) + + resolver = context.getContentResolver() + uri = resolver.insert(MediaStoreDownloads.EXTERNAL_CONTENT_URI, content_values) + + if uri: + try: + input_stream = FileInputStream(File(self.log_file_path)) + output_stream = resolver.openOutputStream(uri) + + buffer = bytearray(8192) + while True: + length = input_stream.read(buffer) + if length <= 0: + break + output_stream.write(buffer, 0, length) + + input_stream.close() + output_stream.close() + toast("Logs exported to Downloads") + except Exception as e: + print("Export error:", e) + toast("Export failed") + else: + toast("Export failed") + else: + downloads_dir = Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_DOWNLOADS + ).getAbsolutePath() + dest_path = os.path.join(downloads_dir, "app_logs.txt") + try: + with open(self.log_file_path, "r") as src, open(dest_path, "w") as dst: + dst.write(src.read()) + MediaScannerConnection = autoclass('android.media.MediaScannerConnection') + MediaScannerConnection.scanFile(context, [dest_path], ["text/plain"], None) + toast("Logs exported to Downloads") + except Exception as e: + print("Export error:", e) + toast("Export failed") def clear_logs(self): self.logs_layout.clear_widgets() diff --git a/app_src/ui/screens/manager.py b/app_src/ui/screens/manager.py index a238463..e32596d 100644 --- a/app_src/ui/screens/manager.py +++ b/app_src/ui/screens/manager.py @@ -28,17 +28,18 @@ def __init__(self, **kwargs): self.app = get_app() self.welcome_screen = WelcomeScreen() self.gallery_screen = GalleryScreen() - self.full_screen = FullscreenScreen() + self.full_screen = FullscreenScreen(images_data=self.gallery_screen.images_data) + self.settings_screen = SettingsScreen() self.settings_screen = SettingsScreen() self.log_screen = LogsScreen() - self.download_apk_screen = DownloadApkScreen() + # self.download_apk_screen = DownloadApkScreen() self.add_widget(self.gallery_screen) self.add_widget(self.full_screen) self.add_widget(self.settings_screen) self.add_widget(self.welcome_screen) self.add_widget(self.log_screen) - self.add_widget(self.download_apk_screen) + # self.add_widget(self.download_apk_screen) self.__register_rotate_listener() # self.__run_rotate_method_for_each_screen("BOTTOM") @@ -85,8 +86,11 @@ def go_to_thumbs(self, _=None): def open_image_in_full_screen(self, index): self.transition = NoTransition() self.current = "fullscreen" - self.full_screen.update_images(index) - self.full_screen.carousel.index = index + self.full_screen.current_tab = self.gallery_screen.current_tab + self.full_screen.index = index + + # self.full_screen.update_images(index) + # self.full_screen.carousel.index = index def on_rotation(self, rotation): rotation=self.__get_rotation_name(rotation) diff --git a/app_src/utils/helper.py b/app_src/utils/helper.py index 4e6fd75..a0a08eb 100644 --- a/app_src/utils/helper.py +++ b/app_src/utils/helper.py @@ -44,7 +44,7 @@ def makeFolder(my_folder): return my_folder -def appFolder() -> str | bytes: +def appFolder() -> str: """Creates (if needed) and returns the Laner download folder path.""" if on_android_platform(): diff --git a/buildozer.spec b/buildozer.spec index 88f7359..4ebcc9d 100644 --- a/buildozer.spec +++ b/buildozer.spec @@ -35,7 +35,7 @@ android.permissions = RECEIVE_BOOT_COMPLETED, INTERNET, VIBRATE, USE_EXACT_ALARM #android.permissions = RECEIVE_BOOT_COMPLETED, INTERNET, VIBRATE, USE_EXACT_ALARM, SCHEDULE_EXACT_ALARM, FOREGROUND_SERVICE, FOREGROUND_SERVICE_DATA_SYNC, POST_NOTIFICATIONS, SET_WALLPAPER, READ_MEDIA_IMAGES, (name=android.permission.READ_EXTERNAL_STORAGE;maxSdkVersion=32), (name=android.permission.WRITE_EXTERNAL_STORAGE;maxSdkVersion=28) android.add_src = %(source.dir)s/android/src android.add_resources = %(source.dir)s/android/res -android.gradle_dependencies = androidx.core:core-ktx:1.12.0, com.google.android.material:material:1.12.0 +android.gradle_dependencies = com.google.android.material:material:1.12.0 #android.gradle_dependencies = com.google.android.material:material:1.6.0, androidx.core:core-ktx:1.15.0, androidx.core:core:1.6.0, androidx.work:work-runtime:2.9.0 android.enable_androidx = True p4a.hook = %(source.dir)s/android/p4a/hook.py @@ -43,7 +43,8 @@ p4a.hook = %(source.dir)s/android/p4a/hook.py -android.api = 35 +android.api = 36 +android.ndk = 29 p4a.branch = develop diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..5fc0312 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +kivy +android-notify +android-widgets +python-osc +https://github.com/Fector101/plyer/archive/feature/file-type-parser.zip +https://github.com/kivymd/KivyMD/archive/master.zip \ No newline at end of file