Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
brand engager
Mar 23, 2011

Trying some stupid poo poo



Edit: it boots now

brand engager fucked around with this message at 02:50 on Mar 8, 2018

Adbot
ADBOT LOVES YOU

kitten smoothie
Dec 29, 2001

At the end of the day even if you got that working, it's still going to be an ARM emulator and slow as hell. I know you can't run an x86 emulator on EC2, but maybe telling jenkins to ship it to firebase test lab is an option for you there if you don't want local physical infrastructure.

brand engager
Mar 23, 2011

Is there any way to change the colors used for specific days in a CalendarView widget? Most of the color related methods are deprecated, and subclassing the widget hasn't exposed anything that can change colors.

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...
You might want to use the material backport on GitHub and customize it in this case. Not sure how far back you need to support or how much you care about device L&F

brand engager
Mar 23, 2011

Volmarias posted:

You might want to use the material backport on GitHub and customize it in this case. Not sure how far back you need to support or how much you care about device L&F

Our minimum API level is at 15 right now. I might just abandon the color thing for now since this is for a class group project.

Splinter
Jul 4, 2003
Cowabunga!
I've been working on a basic app (list view displaying all entries in a DB table, detail view for inserting and updating entries) using the MVVM pattern with Room and LiveData and I've run into a few things I haven't been able to figure out.

I'm trying to have the both the insert and update features use the same code. If the detail activity is passed a valid ID, it fetches the item from the DB and populates the views. If it is passed no ID or an ID that doesn't exist in the DB, it creates a new Item (with default values) and populates the views. My issue right now is when creating a new item, I'm not sure where I should be tying the new item object to a LiveData object. With what I have now (see below), the new item is successfully inserted into the DB when the save button is clicked, but because the activity is still observing LiveData with a null item, the views are all updated to show the default values again after the insert triggers the onChanged callback.

My first instinct was to have the repository's loadItemById method or VM's getObservableItem or init methods check if the LiveData's value is null, and if so, create/return a new LiveData object with the value set to a new Item(). However, it seems DAO queries that return LiveData objects are automatically run asynchronously, so there's no guarantee the LiveData's value will be set when these methods are called even when data for the given ID already exists in the DB.

Another thought I had was to use MutableLiveData instead of LiveData for the detail view, then if getValue returns null in the activity's onChanged callback, call setValue with a new Item. However, Room can't return MutableLiveData, only LiveData (which behind the scenes is returned as ComputableLiveData), and it doesn't seem like this can be cast to MutableLiveData. I believe I could get around this by having the query just return an Item, then creating the MutableLiveData inside the repository, but a) I'm not sure the activity should be concerned with this, and b) I feel like I'm doing something wrong if I'm not able to take advantage of Room's ability to return LiveData objects.

I could also just make a separate workflow that doesn't use LiveData when the detail view is being used to create a new Item, then reload the view with the newly inserted ID after the initial save is complete. Again, I feel like I'm missing an easy way to make my initial approach work.

Here's a simplified version of what I'm working with:

DAO:
code:
@Query("SELECT * FROM items WHERE id = :id")
LiveData<Item> loadById(long id);

@Insert(onConflict = REPLACE)
long insert(Item item);
Repository:
code:
public LiveData<Item> loadItemById(final long id) {
        return mDb.itemDao().loadById(id);
}

public void persist(Item item) {
        new PersistItemTask(mDb).execute(item);
}

private static class PersistItemTask extends AsyncTask<Item, Void, Void> {
        AppDatabase mDb;

        PersistItem(AppDatabase db) {
            mDb = db;
        }

        @Override
        protected Void doInBackground(Item... items) {
            mDb.itemDao().insert(items[0]);

            return null;
        }
}
VM:
code:
private LiveData<Item> mObservableItem;

// ...

public LiveData<Item> getObservableItem() {
        return mObservableItem;
}

public void init(long id) {
        mObservableItem = mDataRepository.loadItemById(id);
}

public void persist(Item item) {
        mDataRepository.persist(item);
}
Activity:
code:
protected void onCreate(@Nullable Bundle savedInstanceState) {
       // ...
        mViewModel = ViewModelProviders.of(this).get(ItemViewModel.class);
        mViewModel.init(itemId);

        mViewModel.getObservableItem().observe(this, new Observer<Item>() {
            @Override
            public void onChanged(@Nullable Item item) {
                if (item == null) item = new Item();
                mItem = item;
                populateViews(); // populates with values from mItem
            }
        });
}

@OnClick(R.id.saveButton)
public void saveButtonClick() {
        hideKeyboard();
        saveItem(); // calls view model's persist method
}

ReverendCode
Nov 30, 2008
It seems like you would know before opening the detailview whether there was an existing item, so you either get a livedata attached to a DB entry, or you create a new DB entry, and then pass the livedata associated with the new entry. Similarly when you update, you should be able to just update the DB directly, and allow the changes to propogate forward to your ui.

Splinter
Jul 4, 2003
Cowabunga!
That could work, but I think the new entry really shouldn't be inserted into the DB until the user saves the entry from the detail view. They aren't committing to creating a new item just by hitting the FAB (e.g. like Google Calendar). I could a) delete the entry if the user back/cancels out of the creation workflow, and b) add a DB flag that marks whether or not an entry is 'live' so that the placeholder inserted into the DB isn't returned by any queries that select all items in the meantime, but that seems like a kinda hacky approach imo.

What I did for now is just not use LiveData when creating a new item, and when the user saves (which inserts the item into the DB), the activity finishes and returns to the list view (so I don't have to worry about getting the ID of the newly inserted item to rebuild the detail view with LiveData). I think not using LiveData for creation makes sense, since there isn't really an data to observe until it is inserted into the DB.

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Shouldn't your ViewModel be handling all that anyway? When the user is entering data into the view it's just there in the UI, it only gets passed to the VM when they press insert or whatever. Then the VM inserts it, gets back some data from the Model and tells the View what to display

Splinter
Jul 4, 2003
Cowabunga!
I believe it does, but maybe I'm missing something. The activity observes the LiveData provided by the VM for changes. On change, the activity gets the new values from the VM and uses them to update the views. When the user hits 'save', the current values of the views are passed to the VM and the VM is in charge of persisting the changes (which it does via a repository class, since I'll eventually have both a web server and the local DB as data sources).

All I was missing with my original approach for the create workflow was how to get a LiveData object with ID back to the VM after performing the insert. The insert query can return the ID of the newly inserted element, but because the insert happens asynchronously, it isn't as straightforward as having the the repository's insert method return a LiveData object tied to the new row. That's what I hadn't figured out how to do yet. It's no longer necessary now that the detail view just returns to the list view after the user saves a new item, but it'd still be nice to know how to do properly.

Volguus
Mar 3, 2009
From the last hour of research it seems that the Google Play Store situation in China is FUBAR. Those of you that are in the know, is that a correct assessment? Am I missing some new information that could improve that assessment (that is, plans for Google to actually provide the real thing there)? Does anyone have any experience with China that's willing to share?

FAT32 SHAMER
Aug 16, 2012



Volguus posted:

From the last hour of research it seems that the Google Play Store situation in China is FUBAR. Those of you that are in the know, is that a correct assessment? Am I missing some new information that could improve that assessment (that is, plans for Google to actually provide the real thing there)? Does anyone have any experience with China that's willing to share?

Is it russia or china that requires you to submit source code to the government for review before you are legally allowed to sell it in their country?

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Splinter posted:

I believe it does, but maybe I'm missing something. The activity observes the LiveData provided by the VM for changes. On change, the activity gets the new values from the VM and uses them to update the views. When the user hits 'save', the current values of the views are passed to the VM and the VM is in charge of persisting the changes (which it does via a repository class, since I'll eventually have both a web server and the local DB as data sources).

All I was missing with my original approach for the create workflow was how to get a LiveData object with ID back to the VM after performing the insert. The insert query can return the ID of the newly inserted element, but because the insert happens asynchronously, it isn't as straightforward as having the the repository's insert method return a LiveData object tied to the new row. That's what I hadn't figured out how to do yet. It's no longer necessary now that the detail view just returns to the list view after the user saves a new item, but it'd still be nice to know how to do properly.

Well I'm no expert on this, but if I'm reading you right you want to do something like this? *fires up the ol notepad.exe*

pre:
enter data -> get item from DB ----------------------------> display item
                     |                                     |
                     -> no item -> insert data as new item -
honestly this feels like unusual behaviour, and it probably belongs in the model as business logic? "Here's some data, give me the item that matches it if one exists, otherwise insert it and then give me that".

You could put it in the ViewModel, exposing a MutableLiveData object (as plain LiveData) for the observer to subscribe to, and keeping Room's LiveData internal to the VM. That way you can only publish 'real' data to your observer, and if your Room query returns null, you can tell it to do an insert instead, and publish that result. Trouble is you then have to hang on to what data you were trying to fetch, and pass it along when you get the empty result, as in "oh it's not there? Insert all this then" and keep track of it all since these are asynchronous events. It's a lot easier if you can add a fetchOrInsert method in your repo, which should always publish one set of valid data, and you can just hand that LiveData object to your observer

You also might want to look at Transformations, which I don't think are a good fit for what you're doing, but it's probably handy to know when you want to tweak the results. It's nowhere near as powerful as something like Rx though

baka kaba fucked around with this message at 21:06 on Apr 13, 2018

Volguus
Mar 3, 2009

FAT32 SHAMER posted:

Is it russia or china that requires you to submit source code to the government for review before you are legally allowed to sell it in their country?

Oi, I have no idea. Never heard of it. Like I've never heard that Google does not do business in China and that there are 400 Android App Stores in there with a handful having the lion's share. drat.

PokeJoe
Aug 24, 2004

hail cgatan


Yeah this thread made me read up a bit on Chinese app stores and lol

sausage king of Chicago
Jun 13, 2001
I've started looking into creating a video editor app and wanted to do it in Xamarin. I've been googling around for the past hour or so, and it seems if I want to do something like this (my first idea is to allow a user to load in a pre-recorded video and split it up into shorter clips) I should be using FFmpeg? I found this library but haven't really looked to much into it yet. Are there better options out there? From what I've read while googling, people are saying FFmpeg is really hard to work with, though most of those posts are from a few years ago. Is that still the case? Am I in for some hurting?

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed
You will want to use something much higher level than ffmpeg itself such as libmpv or libvlc. The libavcodec and libavformat APIs are not realistically usable without in-depth knowledge of how video formats work, and things like frame-accurate seeking in a video file is literally hundreds of lines of code.

FAT32 SHAMER
Aug 16, 2012



This is probably the stupidest and simplest question, but what is the best way to differentiate layouts for the differences in say, the Nexus 7 vs Nexus 9 vs Nexus 10? I started developing my app with the intention of the company purchasing Pixel C's for the consultants who would be using it, however, Google decided to not make the Pixel C anymore + it has a dumbass pixel density + screen ratio. I just finished optimizing the screen layout for the Nexus 9 but I want to make sure it will work on most 7"/9"/10" samsung/google tablets and I'm not sure how to actually test and verify this since there are no plugins for samsung tablets in android studio (that i could find, anyways)

I currently have them differentiating by xhdpi but it turns out the Nexus 7/9/10 are all xhdpi so they will all use the same layout.

Google's Support Different Screen Sizes says

quote:

Use the smallest width qualifier
The "smallest width" screen size qualifier allows you to provide alternative layouts for screens that have a minimum width measured in density-independent pixels(dp or dip).

By describing the screen size as a measure of density-independent pixels, Android allows you to create layouts that are designed for very specific screen dimensions while avoiding any concerns you might have about different pixel densities.

For example, you can create a layout named main_activity that's optimized for handsets and tablets by creating different versions of the file in directories as follows:

res/layout/main_activity.xml # For handsets (smaller than 600dp available width)
res/layout-sw600dp/main_activity.xml # For 7” tablets (600dp wide and bigger)
The smallest width qualifier specifies the smallest of the screen's two sides, regardless of the device's current orientation, so it's a simple way to specify the overall screen size available for your layout.

Here's how other smallest width values correspond to typical screen sizes:

320dp: a typical phone screen (240x320 ldpi, 320x480 mdpi, 480x800 hdpi, etc).
480dp: a large phone screen ~5" (480x800 mdpi).
600dp: a 7” tablet (600x1024 mdpi).
720dp: a 10” tablet (720x1280 mdpi, 800x1280 mdpi, etc).
but that's all using mdpi instead of xhdpi, and iirc when i tried that they automatically went on the phone emulator in the design tab instead of the correct tablet emulators or something like that. Would I just use something like res/layout-sw1200dp/main_activity.xml, res/layout-sw1536dp/main_activity.xml, and res/layout-sw1600dp/main_activity.xml?

e; nope, that defaults to the nexus 4 ughhh

FAT32 SHAMER fucked around with this message at 14:58 on Apr 17, 2018

Tunga
May 7, 2004

Grimey Drawer
Generally, don't have a million different buckets. Make the design flexible and work well on all screen sizes as much as you can.

Having said that, it often makes sense to at least have a phone layout and a tablet layout, and sometimes it makes sense to do three layouts for phones, 7in tablets and 10in tablets. Keep in mind that users can change the DPI on the device so you cannot guarantee anything, but you can make sensible choices and make sure everything scales well to a reasonable degree (bigger and smaller) for each bucket. That should be all you need to do.

As you tried, sw buckets are the easiest way to do this. But your numbers are all way too big to be useful. Use the provided ones. The fact that "that's all using mdpi" is irrelevant because dpi is density independent by definition. So if you definitely need three totally different layouts, start with dp600 and dp720 and see how that works. The you can try dp800 or something if you want to cater to bigger devices in some specific way. But I would really avoid having more than three buckets if you can, you'll just end up in a mess trying to keep them all updated. It also makes testing horrible.

Edit: For the "how do I Samsung tablet on the emulator" just find out the size and resolution and plug the numbers into a custom emulator profile. Be aware that specific devices can have rendering bugs and such that will not show up by doing this. If a certain device will be very common amongst your users, I'd try to get a physical device for testing. But for just checking that the layout sizes properly, you can use the emulator.

Tunga fucked around with this message at 16:10 on Apr 17, 2018

FAT32 SHAMER
Aug 16, 2012



Tunga posted:

Generally, don't have a million different buckets. Make the design flexible and work well on all screen sizes as much as you can.

Having said that, it often makes sense to at least have a phone layout and a tablet layout, and sometimes it makes sense to do three layouts for phones, 7in tablets and 10in tablets. Keep in mind that users can change the DPI on the device so you cannot guarantee anything, but you can make sensible choices and make sure everything scales well to a reasonable degree (bigger and smaller) for each bucket. That should be all you need to do.

As you tried, sw buckets are the easiest way to do this. But your numbers are all way too big to be useful. Use the provided ones. The fact that "that's all using mdpi" is irrelevant because dpi is density independent by definition. So if you definitely need three totally different layouts, start with dp600 and dp720 and see how that works. The you can try dp800 or something if you want to cater to bigger devices in some specific way. But I would really avoid having more than three buckets if you can, you'll just end up in a mess trying to keep them all updated. It also makes testing horrible.

Edit: For the "how do I Samsung tablet on the emulator" just find out the size and resolution and plug the numbers into a custom emulator profile. Be aware that specific devices can have rendering bugs and such that will not show up by doing this. If a certain device will be very common amongst your users, I'd try to get a physical device for testing. But for just checking that the layout sizes properly, you can use the emulator.

Hey thanks! i didnt think about 1536px being 720dp

I believe that Samsung tablets have the same resolution as the nexus tablets that are built into android studio, so hopefully this works out

e; the Samsung Galaxy Tab S3 is using the sw600dp bucket for some reason, even though it has the exact same screen resolution as the Nexus 7

i hate samsung

FAT32 SHAMER fucked around with this message at 19:02 on Apr 17, 2018

Tunga
May 7, 2004

Grimey Drawer

FAT32 SHAMER posted:

e; the Samsung Galaxy Tab S3 is using the sw600dp bucket for some reason, even though it has the exact same screen resolution as the Nexus 7

i hate samsung
Nexus 7 (2013) is 1920x1200. Galaxy Tab S3 is 2043x1536, at least according to a quick Google. Culd be that the S3 is like 780dp or something. Just play with numbers or get a display metrics app from Play and find out the exact numbers.

But also, if that's the bucket it matches then that's the bucket it matches. If it has similar DP width to the N7 then it'll show roughly the same amount of stuff on the screen. That's how the DP system works. Don't try to micromanage this poo poo for every device, that's a fool's game on Android.

Taffer
Oct 15, 2010


Unless you have a very specific market for your app, optimizing for tablets in general is a waste. They're like 0.01% of Android device usage and they all suck. Just make a UI that's pretty flexible and leave it at that. A layout made for landscape on a big phone will work well enough for a tablet, even if it's not totally perfect.

Splinter
Jul 4, 2003
Cowabunga!

baka kaba posted:

Well I'm no expert on this, but if I'm reading you right you want to do something like this? *fires up the ol notepad.exe*

pre:
enter data -> get item from DB ----------------------------> display item
                     |                                     |
                     -> no item -> insert data as new item -
honestly this feels like unusual behaviour, and it probably belongs in the model as business logic? "Here's some data, give me the item that matches it if one exists, otherwise insert it and then give me that".

You could put it in the ViewModel, exposing a MutableLiveData object (as plain LiveData) for the observer to subscribe to, and keeping Room's LiveData internal to the VM. That way you can only publish 'real' data to your observer, and if your Room query returns null, you can tell it to do an insert instead, and publish that result. Trouble is you then have to hang on to what data you were trying to fetch, and pass it along when you get the empty result, as in "oh it's not there? Insert all this then" and keep track of it all since these are asynchronous events. It's a lot easier if you can add a fetchOrInsert method in your repo, which should always publish one set of valid data, and you can just hand that LiveData object to your observer

I've figured it out now, but this is what I was doing, which I don't think is all that unusual:

pre:
@list view -> click existing item -> populate Item from DB -------> display item detail
           |                                  v                  ^
           |                            item not found           |
           v                                  v                  |     
           -> click 'new' FAB -----> populate Item with defaults -
There's no DB insert prior to display in the item not found/new item scenario. The item not found scenario isn't something that should happen. Displaying an error going back to the list view rather than falling back to the new item workflow is acceptable. Not pictured is that saving from the detail view (which triggers a DB insert in the new item scenario) remained in the detail view (rather than going back to the list view).

Before switching to using LiveData, it was super trivial to use virtually the same code path for all scenarios. With LiveData, either I'm not using LiveData in the new item scenario, in which case I'd need to get LiveData after the insert to remain in the detail view (making 'new' a more distinct workflow). Or I'm using MutableLiveData, in which case I need to do extra work in the repo or VM to build the MutableLiveData rather than just passing the LiveData returned by Room to the VM which is then observed by Activity/Fragment.

FAT32 SHAMER
Aug 16, 2012



Tunga posted:

Nexus 7 (2013) is 1920x1200. Galaxy Tab S3 is 2043x1536, at least according to a quick Google. Culd be that the S3 is like 780dp or something. Just play with numbers or get a display metrics app from Play and find out the exact numbers.

But also, if that's the bucket it matches then that's the bucket it matches. If it has similar DP width to the N7 then it'll show roughly the same amount of stuff on the screen. That's how the DP system works. Don't try to micromanage this poo poo for every device, that's a fool's game on Android.

I realized that it IS pulling the sw720dp bucket, but since I had made that bucket for 9” screens it’s got a ton of space in some views.

Spec calls for it to look good on tablets that are common or ones that the company would buy for consultants, and they specifically mentioned the Nexus 7 & 9, Tab S3, and the ZenPad 3S 10 so I gotta figure out a more flexible layout for differentiating the Nexus 9 and the ZenPad/Tab S3 :rip:

Tunga
May 7, 2004

Grimey Drawer

FAT32 SHAMER posted:

I realized that it IS pulling the sw720dp bucket, but since I had made that bucket for 9” screens it’s got a ton of space in some views.
You're making your layouts in a really weird way if this is true. Feel free to post some example XML and we can advise.

FAT32 SHAMER
Aug 16, 2012



Tunga posted:

You're making your layouts in a really weird way if this is true. Feel free to post some example XML and we can advise.

Oh dang i thought I had this part down. Ok so this one is a simple 6 "button" layout that has 1 clickable element and the remaining five are empty dotted-line boxes to placehold features that will be added as various business units get their portion of the app completed:

XML code:
<!-- This DrawerLayout has two children at the root  -->
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- This LinearLayout represents the contents of the screen  -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">


        <!-- The ActionBar displayed at the top -->
        <include
            layout="@layout/action_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <!-- The main content view where fragments are loaded -->
        <FrameLayout
            android:id="@+id/flContent"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <GridLayout
                android:id="@+id/grid_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginLeft="30dp"
                android:layout_marginRight="30dp"
                android:layout_marginTop="30dp"
                android:columnCount="3"
                android:rowCount="2">

                <RelativeLayout
                    android:id="@+id/app_360"
                    android:layout_width="300dp"
                    android:layout_height="280dp"
                    android:layout_marginRight="30dp"
                    android:background="@drawable/app_select_tile_used">

                    <ImageView
                        android:layout_width="135dp"
                        android:layout_height="135dp"
                        android:layout_above="@+id/textView"
                        android:layout_centerHorizontal="true"
                        android:layout_marginBottom="22dp"
                        android:src="@drawable/ic_checkbox_marked_circle_outline" />

                    <TextView
                        android:id="@+id/textView"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentBottom="true"
                        android:layout_centerHorizontal="true"
                        android:layout_gravity="center_vertical"
                        android:layout_marginBottom="39dp"
                        android:text="@string/nav_title1"
                        android:textColor="@android:color/white"
                        android:textSize="30sp" />
                </RelativeLayout>
                <RelativeLayout
                    android:id="@+id/app_6"
                    android:layout_width="300dp"
                    android:layout_height="280dp"
                    android:layout_marginRight="30dp"
                    android:layout_marginTop="30dp"
                    android:background="@drawable/app_select_tile_unused">

                </RelativeLayout>
                <RelativeLayout
                    android:id="@+id/app_6"
                    android:layout_width="300dp"
                    android:layout_height="280dp"
                    android:layout_marginRight="30dp"
                    android:layout_marginTop="30dp"
                    android:background="@drawable/app_select_tile_unused">

                </RelativeLayout>
                <RelativeLayout
                    android:id="@+id/app_6"
                    android:layout_width="300dp"
                    android:layout_height="280dp"
                    android:layout_marginRight="30dp"
                    android:layout_marginTop="30dp"
                    android:background="@drawable/app_select_tile_unused">

                </RelativeLayout>
                <RelativeLayout
                    android:id="@+id/app_6"
                    android:layout_width="300dp"
                    android:layout_height="280dp"
                    android:layout_marginRight="30dp"
                    android:layout_marginTop="30dp"
                    android:background="@drawable/app_select_tile_unused">

                </RelativeLayout>
                <RelativeLayout
                    android:id="@+id/app_6"
                    android:layout_width="300dp"
                    android:layout_height="280dp"
                    android:layout_marginRight="30dp"
                    android:layout_marginTop="30dp"
                    android:background="@drawable/app_select_tile_unused">

                </RelativeLayout>
            </GridLayout>
        </FrameLayout>
    </LinearLayout>

    <!-- The navigation drawer that comes from the left -->
    <!-- Note that `android:layout_gravity` needs to be set to 'start' -->
    <android.support.design.widget.NavigationView
        android:id="@+id/nvView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@android:color/white"
        android:layout_marginTop="25dp"
        app:menu="@menu/drawer_view" />
</android.support.v4.widget.DrawerLayout>

I didnt screencap the action bar because it has the company name in it, but its the blue part that kind of sticks out up top. As you can see, the 9.7" tablets that are 2048x1536 are using the sw720dp view that I made, but because it has more screen real estate, it has a bunch of emptiness in the corner. I know in iOS you are able to make it dynamically fill a screen by using constraints, and I know that Google recently(ish) came out with their own constraintview, but prior to the spec update we were only supporting the Pixel C instead of any given device.

There are more complicated views in the app that involve programmatically injecting ExpandableListView parent/children dynamically that I can share as well, but i wouldnt be able to share a screenshot. This view has two ELVs: one that acts as a sidebar that displays a categories and subcategories, whether the question has been answered, and a score for that category, and another that acts as a question holder that has a question in the ELV parent with a switch to enable and disable it, and 4 boxes that display potential bad, mediocre, good, and world class answers plus a comment section to leave additional comments if need be:

activity_edit_draft.xml
XML code:
<!-- This DrawerLayout has two children at the root  -->
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- This LinearLayout represents the contents of the screen  -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">


        <!-- The ActionBar displayed at the top -->
        <include
            layout="@layout/action_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <!-- The main content view where fragments are loaded -->
        <FrameLayout
            android:id="@+id/flContent"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <RelativeLayout
                android:id="@+id/rlcontent"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <!--SideBar views -->
                <RelativeLayout
                    android:layout_width="325dp"
                    android:layout_height="match_parent"
                    android:background="@color/sidebarColor">
                    <ExpandableListView
                        android:id="@+id/qwedit_sidebar_elv"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:indicatorLeft="@null"
                        android:divider="@color/colorPrimary"
                        android:dividerHeight="0.5dp"
                        android:groupIndicator="@null"
                        >
                    </ExpandableListView>
                </RelativeLayout>

                <!--MainView views -->
                <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                    <ExpandableListView
                        android:id="@+id/qwedit_main_elv"
                        android:layout_width="700dp"
                        android:layout_height="match_parent"
                        android:layout_alignParentEnd="true"
                        android:layout_alignParentTop="true"
                        android:padding="5dp"
                        android:background="@color/white"
                        android:divider="@color/white"
                        android:dividerHeight="10dp"
                        android:childDivider="@color/white"
                        android:indicatorLeft="?android:attr/expandableListPreferredItemIndicatorLeft">
                    </ExpandableListView>
                </RelativeLayout>

            </RelativeLayout>
        </FrameLayout>
    </LinearLayout>

    <!-- The navigation drawer that comes from the left -->
    <!-- Note that `android:layout_gravity` needs to be set to 'start' -->
    <android.support.design.widget.NavigationView
        android:id="@+id/nvView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@android:color/white"
        android:layout_marginTop="25dp"
        app:menu="@menu/drawer_view" />

</android.support.v4.widget.DrawerLayout>
sidebar_elv_parent.xml
XML code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="225dp"
    android:layout_height="match_parent"
    android:padding="8dp">

    <ImageView
        android:id="@+id/group_indicator"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_alignParentStart="true"
        android:layout_centerInParent="true" />

    <TextView
        android:id="@+id/edit_sidebar_elv_parent_title"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_toEndOf="@+id/group_indicator"
        android:padding="3dp"
        android:text="Category"
        android:textColor="@color/black"
        android:textSize="20sp" />

    <ImageView
        android:id="@+id/edit_elv_parent_status"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_centerInParent="true"
        android:layout_marginEnd="10dp"
        android:layout_toStartOf="@+id/edit_elv_parent_score"
        android:src="@drawable/ic_warning" />

    <TextView
        android:id="@+id/edit_elv_parent_score"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_centerInParent="true"
        android:layout_gravity="left"
        android:text="Score"
        android:textColor="@color/colorPrimary"
        android:textSize="20sp" />


</RelativeLayout>
sidebar_elv_child.xml
XML code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/edit_elv_child_title"
        android:layout_width="185dp"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_centerVertical="true"
        android:layout_marginStart="50dp"
        android:gravity="start"
        android:textColor="@color/colorPrimary"
        android:textSize="18sp" />

    <ImageView
        android:id="@+id/edit_elv_child_status"
        android:layout_width="25dp"
        android:layout_height="25dp"
        android:layout_alignParentEnd="true"
        android:layout_marginRight="57dp"
        android:layout_marginBottom="30dp"
        android:layout_centerVertical="true"
        android:src="@drawable/ic_warning" />

</RelativeLayout>
quest_elv_parent.xml
XML code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/edit_main_elv_group_container"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/sidebarColor">

    <TextView
        android:id="@+id/edit_main_elv_parent_title"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_gravity="center"
        android:layout_marginStart="25dp"
        android:padding="8dp"
        android:text="Parent Category"
        android:textSize="20sp"
        android:textColor="@color/black" />

    <Switch
        android:id="@+id/select_question_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_centerVertical="true"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:gravity="center"
        android:text="Enable Question"
        android:layout_marginRight="10dp"
        />


</RelativeLayout>
quest_elv_child.xml
XML code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/edit_main_child_container"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:descendantFocusability="beforeDescendants">
    <LinearLayout
        android:id="@+id/rating_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:weightSum="4">
        <LinearLayout
            android:id="@+id/edit_main_badView"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="125dp"
            android:background="@color/badScore">
            <!-- h=145.3dp -->
            <TextView
                android:id="@+id/edit_main_badTextView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:textColor="@color/white"
                android:textSize="20sp"
                android:text="Bad"/>
        </LinearLayout>
        <LinearLayout
            android:id="@+id/edit_main_mediocreView"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="125dp"
            android:background="@color/mediocreScore"
            >
            <TextView
                android:id="@+id/edit_main_mediumTextView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:textColor="@color/white"
                android:textSize="20sp"
                android:text="Mediocre"/>
        </LinearLayout>
        <LinearLayout
            android:id="@+id/edit_main_goodView"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="125dp"
            android:background="@color/goodScore"
            >
            <TextView
                android:id="@+id/edit_main_goodTextView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:textColor="@color/white"
                android:textSize="20sp"
                android:text="Good"/>
        </LinearLayout>
        <LinearLayout
            android:id="@+id/edit_main_wcView"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="125dp"
            android:background="@color/worldclassScore"
            >
            <TextView
                android:id="@+id/edit_main_worldclassTextView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:textColor="@color/white"
                android:textSize="20sp"
                android:text="World Class"/>
        </LinearLayout>
    </LinearLayout>
    <EditText
        android:id="@+id/edit_main_commentEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/rating_container"
        android:cursorVisible="false"
        android:inputType="text"
        android:hint="Comments"/>
</RelativeLayout>
I WILL NOTE that we are seriously considering converting the question_elv_parent/child's to Google's CardView because the adapter has less moving parts and because using an ELV simply because it can collapse items doesnt seem to be the correct usecase for it, it was mostly to make the consultant happy while we figured everything out since we're all experienced enough with Android to be able to do it, but not to be able to do it well (we all have around 1y professional experience since we were hired out of uni for a new department)

Any feedback would be super appreciated, I know it's a lot to dump on you but imo the best way to learn is to get yelled at by someone who knows what's up so you can do it the correct way and always remember it

FAT32 SHAMER fucked around with this message at 14:38 on Apr 18, 2018

Tunga
May 7, 2004

Grimey Drawer
You have a lot of hardcoded widths which will leave you white space around them. So that's the cause of the problem.

But then the question is what do you want it to look like instead? Scaled items to fill the width? Scaled items to fill the height? Scaled items to fill both dimensions and potentially change shape? Square items with even gaps around the edges? Square items with gaps between them? All of these are doable but you have to define what you want it to do. It could be as simple as setting gravity="center" on the parent view or it might require a more flexible view like ConstraintLayout.

Android devices can be any size and any shape, so the first rule of layouts is to assume nothing and design accordingly.

Tunga fucked around with this message at 15:47 on Apr 18, 2018

FAT32 SHAMER
Aug 16, 2012



Tunga posted:

If you hardcode the widths everywhere then of course you get white space around them.

What do you want it to look like instead? Scaled items to fill the width? Scaled items to fill the height? Scaled items to fill both dimensions and potentially change shape? Square items with even gaps around the edges? Square items with gaps between them? All of these are doable but you have to define what you want it to do. Android devices can be any size and any shape, so the first rule is to assume nothing and design layouts accordingly.

Yeah that makes sense. Scaled items to fill both dimensions is basically all we need since this is tablet only. How do LinearLayout weights work for both dimensions? Do you use horizontal LinearLayouts with vertical LinearLayouts inside of it? Is there a way to dictate "yeah, i want this element to occupy 30% of the screen from the centre"?

Is there a good guide that explains this topic? the android documents are helpful but only to a certain extent

ReverendCode
Nov 30, 2008

FAT32 SHAMER posted:

Yeah that makes sense. Scaled items to fill both dimensions is basically all we need since this is tablet only. How do LinearLayout weights work for both dimensions? Do you use horizontal LinearLayouts with vertical LinearLayouts inside of it? Is there a way to dictate "yeah, i want this element to occupy 30% of the screen from the centre"?

Is there a good guide that explains this topic? the android documents are helpful but only to a certain extent

I don't know if it is a requirement that you design your screens using linear layout, but if you can switch over to ConstraintLayout instead, you might find an easier time, and from what I have read, due to a lack of nesting, ConstraintLayout is a noticeable amount faster (on complex screens) from not having to traverse a bunch of trees.
In addition, some people, myself included find ConstraintLayout to be much easier to read and reason about than a bunch of nested views.

Tunga
May 7, 2004

Grimey Drawer

FAT32 SHAMER posted:

Yeah that makes sense. Scaled items to fill both dimensions is basically all we need since this is tablet only. How do LinearLayout weights work for both dimensions? Do you use horizontal LinearLayouts with vertical LinearLayouts inside of it?
Yes, that's the simplest version of such a layout. It's also possible to do this in a ConstraintLayout using chains in both directions. That gives you more control but is more fiddly to set up. It may be worth doing though, as mentioned above, as it makes the layout easier to modify later. Performance differences are honestly kind of negligible when you're talking about two nested LinearLayouts but for more complicated layouts ConstraintLayout is an improvement.

As a slight aside, I think you can get rid of the FrameLayout in the top file since it just contains one thing. I only looked quickly though so might be missing some nuance of the layout.

FAT32 SHAMER posted:

Is there a way to dictate "yeah, i want this element to occupy 30% of the screen from the centre"?
ConstraintLayout can do this kind of thing.

FAT32 SHAMER posted:

Is there a good guide that explains this topic? the android documents are helpful but only to a certain extent
https://constraintlayout.com/ is good for ConstraintLayout stuff but not always totally up to date. In general the docs and such aren't great for this stuff, I mostly just learnt by loving around with stuff.

FAT32 SHAMER
Aug 16, 2012



ReverendCode posted:

I don't know if it is a requirement that you design your screens using linear layout, but if you can switch over to ConstraintLayout instead, you might find an easier time, and from what I have read, due to a lack of nesting, ConstraintLayout is a noticeable amount faster (on complex screens) from not having to traverse a bunch of trees.
In addition, some people, myself included find ConstraintLayout to be much easier to read and reason about than a bunch of nested views.


Tunga posted:

Yes, that's the simplest version of such a layout. It's also possible to do this in a ConstraintLayout using chains in both directions. That gives you more control but is more fiddly to set up. It may be worth doing though, as mentioned above, as it makes the layout easier to modify later. Performance differences are honestly kind of negligible when you're talking about two nested LinearLayouts but for more complicated layouts ConstraintLayout is an improvement.

As a slight aside, I think you can get rid of the FrameLayout in the top file since it just contains one thing. I only looked quickly though so might be missing some nuance of the layout.
ConstraintLayout can do this kind of thing.

https://constraintlayout.com/ is good for ConstraintLayout stuff but not always totally up to date. In general the docs and such aren't great for this stuff, I mostly just learnt by loving around with stuff.

Excellent, thanks so much. I havent had a whole lot of time to gently caress around with this because we were doing a bare bones version of this before to show that we had the capability to do it, and now the business unit leads have determined that 1) we can definitely do it and so far the result is great 2) we want to flex this new (to the company) competency and move away from shared excels to modernize the consultancy business, so now i actually have time in the budget to dick with this stuff

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

You might want to check this out too
https://material.io/guidelines/layout/responsive-ui.html

FAT32 SHAMER
Aug 16, 2012



Is there a way to choose a font size dynamically for a given view? or will that be the main thing that drives me to use buckets now that i have figured out (and fallen in love with) ConstraintViews

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

You can do this if that's what you mean
https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview.html

FAT32 SHAMER
Aug 16, 2012




:doh:

nice, thanks

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

I haven't done any Android dev in years, but I'm wanting to set something up for my home automation setup that involves getting screenshots from my shield tv.

Is it going to be possible or crazy difficult to develop an Android TV app that serves up a screenshot of the screen via http on my local network?

kitten smoothie
Dec 29, 2001

Thermopyle posted:

I haven't done any Android dev in years, but I'm wanting to set something up for my home automation setup that involves getting screenshots from my shield tv.

Is it going to be possible or crazy difficult to develop an Android TV app that serves up a screenshot of the screen via http on my local network?

Check out the MediaProjection APIs, it might do what you want.

https://developer.android.com/reference/android/media/projection/MediaProjection.html

Google also has a sample: https://github.com/googlesamples/android-ScreenCapture

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Oh, that looks interesting, I will dig deeper. Thanks!

FAT32 SHAMER
Aug 16, 2012



Is there a way to pan to an edittext when you're using a constraintlayout? The keyboard covers some of my EditTexts and all of the solutions online say to stick it in a scrollview. Is there a better solution than that?

Adbot
ADBOT LOVES YOU

Tunga
May 7, 2004

Grimey Drawer

FAT32 SHAMER posted:

Is there a way to pan to an edittext when you're using a constraintlayout? The keyboard covers some of my EditTexts and all of the solutions online say to stick it in a scrollview. Is there a better solution than that?
You can chose between a few different keyboard-triggered resizing behaviours by setting a flag on the activity in the manifest, see here:
https://developer.android.com/guide/topics/manifest/activity-element#wsoft

It sounds like you want adjustPan. But be careful with these and make sure your layout works well on different device sizes.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply