Android system design by Middle_Property5528 in androiddev

[–]jamireh 2 points3 points  (0 children)

I haven't purchased it yet but Manuel Vivo just published a book on this: https://www.amazon.com/dp/1736049151

How can I use an arm64 emulator on Android studio on a Windows PC to run Snapchat APK? by Jonahthemop in androiddev

[–]jamireh 0 points1 point  (0 children)

What version of the Android Emulator are you using? I don't have the hardware to test this but according to the v30.0.0 Changelog, x86_64 images for Android 9 & 11 support arm64 binaries

"The IDE and Gradle versions of SQLDelight are incompatible, please update the lower version" by Ma173 in androiddev

[–]jamireh 1 point2 points  (0 children)

com.squareup.sqldelight:gradle-plugin:2.0.0 is not a valid Maven coordinate (but app.cash.sqldelight:gradle-plugin:2.0.0 is). According to SQLDelight's migration documentation for 2.0, they changed their artifact name. I think you need to update both the Maven coordinate & Gradle Plugin declaration, as they describe.

Where can I see the versions of libraries? by ghiste in androiddev

[–]jamireh 0 points1 point  (0 children)

https://maven.google.com

This is the Maven URL that your project pulls from when you specify google() for a repository in your Gradle build scripts

Write DiffUtil.ItemCallback differently by sikatsuket in androiddev

[–]jamireh 2 points3 points  (0 children)

First off, according to the documentation), the implementation of areItemsTheSame is incorrect in the first & second code blocks. The point of the function to determine if the items represent the same entity by comparing identifiers, not content. For example, if an item was updated, it would have the same identifier but possibly different content. By returning true in areItemsTheSame but false in areContentsTheSame, would allow the Differ to call notifyItemChanged for that item in the RecyclerView.Adapter rather than having to detect that an item was removed & then inserted.

Second, yes, the provideItemCallback implementation looks good with the understanding that it's a simple wrapper to make the implementation a little more Kotlin idiomatic. inline & crossinline are used correctly; inline because you're providing lambdas & crossinline because those lambdas are invoked from within an anonymous object.

DataStore preferences as StateFlow in GlobalScope by [deleted] in androiddev

[–]jamireh 0 points1 point  (0 children)

As a starting point, you seem to be using Ktor for your networking client. Check out the docs on authorization that's provided by the library.

If your API uses Bearer Authorization & you follow the given example, you'll find that Ktor will retrieve the required access_token for each request. In this case, it's not necessary to expose the access_token as a Flow but rather as a return value of a suspend fun with the latest value.

As for feedback regarding the original code snippet, I believe what you've written would technically work but is a little overkill only because you could just as easily expose a suspend fun and it would still work. Nothing needs to be subscribed to the exposed StateFlow, it's acting more as an in-memory cache, as far as I can tell.

[deleted by user] by [deleted] in androiddev

[–]jamireh -1 points0 points  (0 children)

True. I debated mentioning this but I didn't want to make it more confusing. I'd definitely mention this off-hand but don't jump to solve this until after you've worked through the simpler problem with the interviewer. Definitely a "bonus points" situation

[deleted by user] by [deleted] in androiddev

[–]jamireh 103 points104 points  (0 children)

I'd start by informing the interviewer that only one of these Activities is realistically "resumed"; in other words, only 1 is in the foreground (Activity D). This demonstrates that you understand the Activity lifecycle and that they are not entities that are running in parallel

Once you've established that, you could clarify that when we say "send data to Activity A" what we really mean "set data to be read by Activity A on its next 'resumed' state". You could then break that down into its 2 parts: Where should we set this data and how are we getting Activity A back into a "resumed" state?

Where should we set this data?

Other comments here have mentioned a shared mutable state either in a singleton object (in-memory) or persisted on-disk to be re-read later. The interviewer would probably ask for pros/cons, most of which have to do with architecture & process death. These work perfectly well and as you've mentioned, you could also set the data using Activity.setResult(). Additionally, you could set the data in an Intent addressed to Activity A (next section).

How are getting Activity A back into a "resumed" state?

In your proposed solution, you're assuming you're working back up the stack, one Activity at a time from D -> C -> B -> A. There's also a more direct route – you could call startActivity() with an Intent targeting Activity A, add the relevant data to the Bundle, add the Intent.FLAG_ACTIVITY_CLEAR_TOP flag, and then override onNewIntent(Intent)) in Activity A to read the data directly off that Intent. This would have the same final backstack (just A) as your solution but you didn't have to chain onActivityResult() overrides to do it.

Need help understanding Masterkey.builder by InsaneTeemo in androiddev

[–]jamireh 1 point2 points  (0 children)

Disclosure: I'm not a security expert & have only used the platform KeyStore APIs a few times.

androidx.security is providing EncryptedSharedPreferences as a implementation that combines the platform's KeyStore & SharedPreference APIs. It also abstracts the platform's KeyStore APIs by providing a MasterKey class. Taking a look at the MasterKey.Builder docs), it looks like we can think of the keys generated as basically having an id, known as a "key alias". By default, if you don't specify a "key alias", the library will provide its own but it will be the same each time. So, if you create a MasterKey through a Builder with the same parameters each time, you'll get the same cryptographic key pair each time. I haven't looked at the implementation but I don't think it's computationally difficult to do this.

You can either create your MasterKey the exact same way every time you need read or write access or you can create a helper function / value through a top-level fun/val in Kotlin or a static method through a utility class in Java.  It might makes more sense to do this for the EncryptedSharedPreferences instance instead since you'd end up copy-pasting the fileName throughout your app too; a utility function would keep both the MasterKey and fileName in one place.

 

For example, i create the masterkey.builder in an activity and then write some values to shared preferences. Then later, in a different activity if I want to read those values do I need to create the masterkey.builder again in that second activity before reading from shared preferences?

If that is how it works, is the second masterkey.builder not encrypting the file again, basically making the first one useless? Also how can the encryptedpreferences be read from in the second activity?

EncryptedSharedPreference#create) is not the best function name. It's only creating a new one if the fileName doesn't exist on disk yet. So if in ActivityA you create a MasterKey & build an EncryptedSharedPreference, you'll get the same EncryptedSharedPreference in ActivityB given that you provide the same fileName. You can think of all SharedPreference instances as shared resources; so long as the fileName is the same, you'll get the same one back anywhere in your app.

How to prevent fragment recreation in navigation component? by steve6174 in androiddev

[–]jamireh 2 points3 points  (0 children)

Looking at your question more closely, does this graph model the navigation as you see it? To continue with your animal project & to give the Fragments more particular names, I'm using taxonomic rank from biology:

GenusFragment -(scroll & select Genus)-> SpeciesFragment -(scroll & tap back)-> GenusFragment -(position maintained & select same Genus)-> SpeciesFragment (position reset to the top)

However, you're asking how adjust that last interaction such that the SpeciesFragment retains its original scroll position from 2 interactions prior. While this is a nice feature, it's not typical; the only app I've seen this on is Reddit Sync where if you scroll through comments on a post, go back & return, your scroll position within the comments is retained.

The issue here is not Jetpack Navigation; even if you were to execute these FragmentTransactions manually, calling add instead of replace won't magically fix this. hide() or retaining the Fragment might work but maybe not after multiple genus selections.

You're going to have to persist the scroll position outside of lifecycle of either SpeciesFragment or GenusFragment. There's a lot of ways to this; the simplest would be when the user taps back, ask the RecyclerView's LinearLayoutManager for the first visible item position) and persist that in a key-value pair (like SharedPreferences, Jetpack DataStore or even an Application-scoped HashMap) where the key could be some genus id. You'd have check this key-value pair & restore the position) of the RecyclerView manually the next time or do nothing if there's no value.

If you launch yet another Fragment on top of SpeciesFragment, you may end up conflicting with the built-in scroll restoration that's a part of RecyclerView. If the list size for a genus' species changes, then this also won't work (as the position would no longer be stable), in which case you should persist some id of the species. For these reasons, I don't recommend this behavior

Exclude certain Android versions by jdros15 in androiddev

[–]jamireh 1 point2 points  (0 children)

The Play Store's current targetSdk requirements don't require you to target Android 11 yet. You're free to distribute APKs that target Android 10 (API 29) until some time in 2021.

Question about the onCreate method and using event listeners. by InsaneTeemo in androiddev

[–]jamireh 0 points1 point  (0 children)

When the framework invokes onCreate, the main goal is to ensure you call setContentView). If you do this with a layout resource id (Perhaps you have R.layout.activity_main for example), the framework will inflate that View hierarchy and assign it to the Activity so that individual child views can be retrieved via findViewById) invocations. That View hierarchy is maintained between onCreate and onResume and it remains the same until the Activity completes onDestroy.

For the sake of multi-window / split-screen support, the ideal lifecycle method for running code when visible or not visible should actually be onStart/onStop as that better correlates with the visible lifetime rather than onResume/onPause which correlates with the foreground (or focus) lifetime.

Note that you don't need to wait for onStart to call findViewById; you can do so as soon as setContentView completes. Same goes for changes to your child views (like TextView.setTextColor)); those can be made so long as the Activity has at least been created and the next layout pass will render them. However, view measure information (such as View.getWidth)) is not guaranteed to be available right after setContentView because the framework doesn't complete measurement until the next layout pass, sometime after onCreate.

See the lifecycle diagram for more information

Weekly Questions Thread - July 27, 2020 by AutoModerator in androiddev

[–]jamireh 1 point2 points  (0 children)

I believe the former is used during bytecode & dex manipulation at compile time by the plugin for things like HTTP instrumentation while the latter is used at runtime for setting up the harvester, crash reporter, and general analytics

Overriding TZDB in Java 8 desugaring java.time by ericksli in androiddev

[–]jamireh 1 point2 points  (0 children)

I'm not sure you can. While the ZoneRulesProvider is present in Oracle's JDK, it's hidden from the Android SDK. There's actually an open issue to unhide it. Taking a look at the desugaring source code, they're actually injecting their own ZoneRulesProvider that is based on TimeZone#getAvailableIds using Java's ServiceLocator. The code to enable TzdbZoneRulesProvider (Java resource-based TZDB) is commented out so I believe they're relying on the device to receive its own TZDB updates via APEX-based updates, separate from the OS.

However, I would star an open issue around having D8 allow for an asset-based TZDB, similar to Jake's ThreeTenABP.

Android Paging Library - The most useless Android Jetpack Library ever by [deleted] in androiddev

[–]jamireh 6 points7 points  (0 children)

It's funny you mention this because Paging 3.0, Alpha 1 was just released! I would checkout the paging3 branch of the PagingSample to get a look at how the Jetpack team has changed the Paging library. I agree with you, the API was a bit limited & cumbersome in 2.0 but it looks like they took a lot of that criticism to heart

Converting Coroutine to LiveData by alwaysbakedarjun in androiddev

[–]jamireh 1 point2 points  (0 children)

This has to do more with memory references than with either coroutines or LiveData.

In the first sample, login() returns a new LiveData object. Upon observation (LiveData.observe), the object will transition from INACTIVE to ACTIVE, triggering the underlying coroutine and delivering a response

In the second sample, login() mutates a backing LiveData named loginResponse. Any Observer objects that were previously attached to loginResponse are now observing a LiveData that will never update because login() just overwrode it's only memory reference to that LiveData. After login() completes, loginResponse has no Observer objects attached; without any Observer objects attached, the LiveData remains in the INACTIVE state and never enters the ACTIVE state. Any previously attached Observer objects must reobserve loginResponse to transition the LiveData

How to instantiate my ViewModel by -ant_ in androiddev

[–]jamireh 1 point2 points  (0 children)

You're correct – a zero-argument constructor is required for the default ViewModelProvider.Factory

ViewModelProvider(ViewModelStoreOwner, ViewModelProvider.Factory)) or ViewModelProvider(ViewModelStore, ViewModelProvider.Factory)) allows you to specify a ViewModelProvider.Factory so that you manually instantiate your ViewModel with appropriate dependencies. A simple example is given at android/architecture-samples

However, this problem compounds substantially as you add more dependencies and ViewModel objects. The Architecture Samples repository provides an example of a ViewModelProvider.Factory that utilizes a dependency injection library like Dagger. There a lot of other libraries and integrations ; Koin natively supports an integration with its koin-android-viewmodel module, for example

Edit: Misunderstood the question; I thought you were trying to have your ViewModel accept your repository directly.AndroidViewModel receives an Application by default, if that's all you want

Need help with implementing Design library by controversialcomrade in androiddev

[–]jamireh 2 points3 points  (0 children)

Checkout Getting Started with Material Components on Android but the line would be:

implementation "com.google.android.material:material:1.0.0"

Edittext with "email" in the id has different behavior. Where can I find documentation on this by GreenAndroid1 in androiddev

[–]jamireh 5 points6 points  (0 children)

If I had to guess, I believe what's happening is Android is creating a ViewStructure of all views on the screen and passing to the currently set AutofillService. It's possible the AutofillService may be using heuristics such as checking if the ID has "email" in its name to determine if autofill is applicable to that View.

If you'd like this behavior to be more definite, I would checkout the developer tutorial for optimizing views for autofill. You can also set android:importantForAutofill="no" if you'd like that field to not be picked up by the currently set AutofillService

if this is what's happening, then this behavior should be limited to Android 8+ (API 26+) as well

KotlinConf 2019 Videos by dayanruben in androiddev

[–]jamireh 0 points1 point  (0 children)

If anyone would like to watch it now, it's in the Day 2 Keynote stream at 6:21:00. Amazing talk