beeware / briefcase

Tools to support converting a Python project into a standalone native application.
https://briefcase.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
2.66k stars 372 forks source link

Android emulator stuck on a black screen #569

Closed tomhosker closed 3 years ago

tomhosker commented 3 years ago

Describe the bug Why I run my app using briefcase run, it works as expected, however, when I run it in an Android emulator using briefcase run android, all it gives me is a black screen.

To Reproduce

  1. Make a new BeeWare app following the same steps as in the tutorial.
  2. Insert my code into the project. (See below.)
  3. Run briefcase create android and briefcase build android.
  4. Run briefcase run android.

As for my code, I'm happy to share as much of this as necessary. However, for the sake of brevity, here's my app.py to begin with:

import toga
from toga import Box, MainWindow
from toga.fonts import Font, SANS_SERIF, SERIF
from toga.style import Pack
from toga.style.pack import COLUMN, ROW

from sibylline.dimensions import NUMBER_OF_COLUMNS, NUMBER_OF_ROWS
from sibylline.custom_placement_menu import CustomPlacementMenu
from sibylline.recommendations_menu import RecommendationsMenu
from sibylline.mini_erebus.arrivals_manager_daughter import \
    ArrivalsManagerDaughter
from sibylline.mini_erebus.departures_manager_daughter import \
    DeparturesManagerDaughter

TITLE_FONT_SIZE = 30
STANDARD_PAD = 5

class Sibylline(toga.App):
    def __init__(self, internal=False, delete_existing=False):
        super().__init__()
        self.internal = internal
        self.delete_existing = delete_existing

    def startup(self):
        self.recs_dictionary = dict()
        self.columns = NUMBER_OF_COLUMNS
        self.rows = NUMBER_OF_ROWS
        self.arrivals_manager = ArrivalsManagerDaughter(self)
        self.departures_manager = DeparturesManagerDaughter(
                                      internal=self.internal,
                                      delete_existing=self.delete_existing)
        self.recs_menu = RecommendationsMenu()
        self.custom_placement_menu = None
        self.main_box = Box()
        self.main_box.add(self.recs_menu.display)
        self.main_window = MainWindow(title=self.formal_name)
        self.main_window.content = self.main_box
        self.demo()
        self.main_window.show()

    def add_recommendation(self, recommendation):
        self.recs_dictionary[recommendation["ticket"]] = recommendation
        self.recs_menu.renew_recs_table()

    def remove_recommendation(self, ticket):
        del self.recs_dictionary[ticket]
        self.recs_menu.renew_recs_table()

    def switch_to_custom_placement(self, recommendation):
        self.custom_placement_menu = CustomPlacementMenu(
                                         recommendation, self.rows,
                                         self.columns)
        self.main_box.remove(self.recs_menu.display)
        self.main_box.add(self.custom_placement_menu.display)

    def switch_back_to_recommendations(self):
        self.main_box.remove(self.custom_placement_menu.display)
        self.main_box.add(self.recs_menu.display)

    def demo(self):
        rec = { "ticket": 1, "epc": "epc0", "column": 0, "row": 0 }
        self.add_recommendation(rec)

def main():
    result = Sibylline(internal=True, delete_existing=True)
    return result

Expected behavior I expected my app to run within the Android emulator in a similar fashion to how it runs within Linux itself.

Screenshots This is what my app looks like when run within Linux itself: Screenshot from 2021-02-18 11-39-17

And this is what I get when I run the same code within an Android emulator. Screenshot from 2021-02-18 11-40-36

Environment:

Additional context I've also raised this issue as a Stack Overflow question.

paulproteus commented 3 years ago

@tomhosker Hi! I'd be grateful if you can install the full Android SDK from the Android website and see if a sample app launches properly in Android Studio. It'll help us diagnose at what layer the problem lies.

thomaslillo commented 3 years ago

Hi this is my first submission so I'm not sure if I should be adding on or creating a new issue.

I'm getting the same issue, I can run the app successfully on windows but when I run it on Android the app opens but the screen remains blank.

I just wanted to provide an additional example of an app where this is occurring, although it may be for a different reason.


"""
A simple character generator for new players to D and D 5e, using questions about themselves or their ideal in game personality to create a character.
"""
# widget toolkit
import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW, RIGHT, CENTER
import csv
import random
import sys
from functools import partial

class Mdict:
    def __init__(self):
        self.d = {}
    def __getitem__(self, key):
        if key in self.d:
            return self.d[key]
        else:
            raise KeyError(key)
    def add_key(self, prefix, suffix):
        if prefix in self.d:
            self.d[prefix].append(suffix)
        else:
            self.d[prefix] = [suffix]
    def get_suffix(self,prefix):
        l = self[prefix]
        return random.choice(l)

# each toga app has a single toga.app instance, representing the entity that is the application
class CharacterWizard(toga.App):

    # the startup method for the app
    def startup(self):
        """
        Construct and show the Toga application.

        Usually, you would add your application to a main content box.
        We then create a main window (with a name matching the app), and
        show the main window.
        """
        # the main box is defined - toga apps are comprised of boxes or widgets with styles applied, all within the main box
        main_box = toga.Box(style=Pack(direction=COLUMN)) # creating the main box with a style
        # COLUMN box - that is, it is a box that will consume all the available width, and will expand its height as content is added, but it will try to be as short as possible.

        """ the logic and creation of the questions box
        """
        # the question box that will hold all the rows of questions
        questions_box = toga.Box(style=Pack(direction=COLUMN, padding=5))

        # label at the top with instructions
        instructions_label = toga.Label(
            """
            Welcome to the D&D 5e character name wizard! This is like a little tool that will help you select a brand new name for any character, 
            whether it be an NPC or player, by the theme of the game. These names are created using a Markov Chain from lists of existing names 
            from that theme! This ensures that they are new to you and your fellow players! Press a button below to generate a name.
            """,
            style=Pack(padding=(2))
        )

        # store the list of names in a list
        self.names = []

        # make this reading files into a function 
        #questions = read_csv('..\\data\\RaceQs.csv')
        with open('..\\data\\namelist.csv', mode='r', encoding='utf-8-sig') as raw_file:
            for line in csv.reader(raw_file):
                self.names.append(line)

        # clean up the blanks in the lists of names
        for category in self.names:
            while '' in category:
                category.remove('')

        # the buttons that display a name based on its category
        # ADD BUTTONS HERE WHEN YOU ADD A LIST

        button_box = toga.Box(style=Pack(direction=COLUMN, padding=5))

        # loop through all the lists and create a button for each
        for category in self.names:
            # create the button names
            button = toga.Button(category[1],on_press= partial(self.CreateNames,index=int(category[0])), style=Pack(padding=5))
            # add the button tot he box
            button_box.add(button)

        # display the name to the screen (this will be updated with the new names)
        output = toga.Box(style=Pack(direction=ROW,  padding=5))
        output.add(toga.Label('Randomly Generated Name', style=Pack(text_align=RIGHT)))
        self.display_name = toga.TextInput(readonly=True)
        output.add(self.display_name)

        """ write to the questons box - instructions first then questions then results boxes
        """
        questions_box.add(instructions_label)
        questions_box.add(button_box)
        questions_box.add(output)
        """ adding things to the main box and adding the main box to the window
        """
        main_box.add(questions_box)

        # this is where we define a window to put the main box into
        self.main_window = toga.MainWindow(title=self.formal_name) 
        self.main_window.content = main_box # we add the main box to the window
        self.main_window.show() # show the main window    

    # the display name button widgets
    def CreateNames(self, widget, index):
        # call the generate_names function with the elven subset of self.names
        self.clean_names = self.names[index]
        # set the list to be everything except first two columns
        self.selected_list = self.clean_names[2:]
        name = self.build_dict()
        #print(name)
        self.display_name.value = name

    def build_dict(self, chainlen = 2):
        """
        Building the dictionary
        https://towardsdatascience.com/generating-startup-names-with-markov-chains-2a33030a4ac0 <- how I learned to do this
        """

        if chainlen > 10 or chainlen < 1:
            print("Chain length must be between 1 and 10, inclusive")
            sys.exit(0)

        self.mcd = Mdict()
        oldnames = []
        self.chainlen = chainlen

        # use the selected list of names
        for l in self.selected_list:
            l = l.strip()
            oldnames.append(l)
            s = " " * chainlen + l
            for n in range(0,len(l)):
                self.mcd.add_key(s[n:n+chainlen], s[n+chainlen])
            self.mcd.add_key(s[len(l):len(l)+chainlen], "\n")

        return self.New()

    def New(self):
        """
        New name from the Markov chain
        """
        prefix = " " * self.chainlen
        name = ""
        suffix = ""
        while True:
            suffix = self.mcd.get_suffix(prefix)
            if suffix == "\n" or len(name) > 9:
                break
            else:
                name = name + suffix
                prefix = prefix[1:] + suffix
        return name.capitalize()

# this main method creates the instance of our application - imported and invoked by __main__.py
def main():
    return CharacterWizard()

I am able to run the app in dev mode.

image

But when I use the Beeware emulator I encounter this, the app opens but the screen is blank.

image

freakboy3742 commented 3 years ago

@thomaslillo "Blank app window but showing an app titlebar" is a very different result to "black screen", so it's unlikely your bug is the same as the original reporter.

The next step to diagnose what you're seeing will be to look at the adb logs; you might see errors (or at least some pointers in the right direction) there. Given that the app is running in dev mode, it would suggest the issue is with the Android backend, and possibly a feature that is not implemented or not working as expected.

freakboy3742 commented 3 years ago

Closing due to lack of activity.

AndresRReina commented 2 years ago

In case it helps someone: The "Blank app window but showing an app titlebar" issue happened to me and was solved by deleting the "android" folder in the project root and letting briefcase regenerate it. I believe an old file from a previous build was stuck there, as my previous build was indeed a blank app.

thomaslillo commented 2 years ago

I'll test that out in my project as well, thank you for the comment