A lightweight "about screen" library to allow quick but customizable attribution in Android apps.

Overview

Attribouter

Attribouter

Attribouter is a lightweight "about screen" for Android apps, built for developers to easily credit a project's contributors & dependencies while matching the style of their app. It ships with the ability to fetch metadata directly from GitHub, GitLab, or Gitea (see: git-rest-wrapper), allowing contributors and licenses to be updated or modified without explicit configuration.

JitPack Build Status Discord Liberapay Documentation

Screenshots

Contributors Contributor Licenses License Night Theme
img img img img img

APK

A demo apk of the sample project can be downloaded here.

Usage

This library is published on JitPack, which you can add to your project by copying the following to your root build.gradle at the end of "repositories".

allprojects {
  repositories {
    ...
    maven { url 'https://jitpack.io' }
  }
}

To add the dependency, copy this line into your app module's build.gradle file.

implementation 'me.jfenn:Attribouter:0.1.8'

Starting an Activity

This is pretty simple.

attribouterActivity {
    withFile(R.xml.attribouter)
    withTheme(R.style.AttribouterTheme_DayNight)
    withGitHubToken("abc123")
}
Java

Attribouter.from(context)
    .withFile(R.xml.attribouter)
    .withTheme(R.style.AttribouterTheme_DayNight)
    .withGitHubToken("abc123")
    .show();

Creating a Fragment

This is also pretty simple.

val fragment = attribouterFragment {
    withFile(R.xml.attribouter)
    withTheme(R.style.AttribouterTheme_DayNight)
    withGitHubToken("abc123")
}
Java

Attribouter.from(context)
    .withFile(R.xml.attribouter)
    .withTheme(R.style.AttribouterTheme_DayNight)
    .withGitHubToken("abc123")
    .show();

When using the fragment with R.style.AttribouterTheme_DayNight (the default theme value), be sure that your activity also uses a dark theme in the -night configuration, or you will have problems with text contrast (the fragment does not have a background, so the parent activity's window background will be drawn behind it). You can also call withTheme(R.style.AttribouterTheme) (light) or withTheme(R.style.AttribouterTheme_Dark) to change this behavior.

Things to Note

Request Limits

This library does not use an auth key for any REST APIs by default. It does cache data to avoid crossing GitHub's rate limits, but if your project has more than a few contributors and libraries or you want it to have access to a private repository, you will need to provide an auth token by calling withGitHubToken(token) on your instance of Attribouter. For GitLab/Gitea instances, tokens can be provided per-hostname - for example, withToken("code.horrific.dev", token).

Be careful not to include these token with your source code. There are other methods of providing your token at build-time, such as using a BuildConfig field with an environment variable, that can prevent this from being an issue. These tokens aren't especially dangerous without any scopes/permissions, but GitHub will automatically deactivate them if they show up in any commits/files on their services, which could cause problems for Attribouter.

Configuration

By default, Attribouter will use the configuration file at res/xml/attribouter.xml. You can either name your configuration file "attribouter.xml" to override the resource, or name it differently and call withFile(R.xml.[name]) on your instance of Attribouter instead.

The configuration file consists of a single root element, <about>, with many child elements that can be placed any amount of times in any order, the same as views in a layout file. These elements, called "wedges" in this library for no apparent reason, are created by Attribouter and added to the page in the order and hierarchy that they are defined in. To create your configuration file, you can either use the file from the sample project as a template or use the documentation to write your own.

Proguard / Minification

For those using the R8 compiler, Attribouter's proguard rules should be conveniently bundled with the library already - otherwise, you will need to add them to your app's proguard-rules.pro file yourself to prevent running into any issues with minifyEnabled and the like.

Unfortunately, Attribouter still doesn't behave well with shrinkResources, as the compiler cannot detect references from Attribouter's config file and will exclude them from compilation. There is a workaround to this, however - create a <resources> tag somewhere in your project, and specify tools:keep="@{resource}" for all of the strings and drawables referenced by your config file. For all of Attribouter's own resources, this has already been done - and if you are not referencing any other resources in your configuration, then there shouldn't be an issue.

Used in

If you're using Attribouter in your project, feel free to reach out / make a PR to add it to this list!

How to Contribute

I try to maintain my libraries to meet the needs of all their users - so, to that extent, most contributions will be accepted so long as they represent some kind of functional improvement. I'd much prefer to work together and resolve an issue than turn any genuine effort away. To that end, if you need help with this process, have any questions or confusion, or want to get feedback before a contribution, please don't hesitate to get in touch. (either discord or email work fine)

This repository has two persistent branches: main and develop - of the two, most pull requests should be made to the latter. main will always contain the source code of the current stable release, so any new changes should be merged into develop first. The exception to this is any changes to metadata: the README, documentation, code of conduct, etc. - since these don't affect the compiled program, it makes sense to merge these into main immediately, unless they are tied to a change in functionality (changes in a new version of the library, for example).

Example contributions

  • Development: Developers can help Alarmio by fixing bugs, implementing features, or helping to debug & research new issues. I'm hoping to write a complete guide to this process in the future - for now, please refer to CONTRIBUTING.md.
  • Design: Attribouter should be intuitive and accessible to a wide variety of users - suggestions to improve certain interfaces are always welcome. This includes compatibility with screen readers, problems with contrast / color blindness, and the sizing/positioning of touch targets in the UI - many of which are shamefully untested in its present state.
  • Localization: If Attribouter doesn't have support for your fluent language(s), please consider translating it! Most in-app text is stored in strings.xml - this file should be copied to ../values-{lang}/strings.xml when translated. (this is an absurdly concise explanation - if this isn't clear, simply sending us translations in a new issue or email is perfectly fine!)
  • Documentation: Writing guides and explanations of how Attribouter works, how to use it, and how to contribute to it can go a long way to ensuring its usefulness and stability in the future. Whether this involves an update to the README, a tutorial for users and contributors, or adding Javadocs & comments to undocumented parts of the codebase - anything is valid!

Acknowledgements

Huge thanks to everyone that's helped with this library, directly or otherwise!

Also, mega props to Kevin Aguilar and 221 Pixels for helping improve the library's design & interface.

Development

Design

Comments
  • Feature request: theme support

    Feature request: theme support

    Overwriting @style/AttribouterTheme is an option but it has its limitations if you have multiple themes in your app which can be switched. I tried creating a separate Activity and then attach an About fragment to it which is great because you can switch the theme in onCreate.

            if (savedInstanceState == null) {
                Fragment fragment = Attribouter.from(this).withFile(R.xml.about).toFragment();
    
                FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
                ft.replace(R.id.fragment, fragment);
                ft.commitAllowingStateLoss();
            }
    

    Unfortunately the theme is not maintained to a section with overflow "0" which opens a new view. Is there a solution for this?

    bug enhancement 
    opened by adrcotfas 9
  • InflateException with 0.1.7 using AboutFragment

    InflateException with 0.1.7 using AboutFragment

    After updating from 0.1.6 I'm getting the following error:

    E/AndroidRuntime: FATAL EXCEPTION: main
        Process: de.psdev.devdrawer.debug, PID: 13426
        android.view.InflateException: Binary XML file line #2 in de.psdev.devdrawer.debug:layout/attribouter_item_app_info: Binary XML file line #2 in de.psdev.devdrawer.debug:layout/attribouter_item_app_info: Error inflating class <unknown>
        Caused by: android.view.InflateException: Binary XML file line #2 in de.psdev.devdrawer.debug:layout/attribouter_item_app_info: Error inflating class <unknown>
        Caused by: java.lang.reflect.InvocationTargetException
            at java.lang.reflect.Constructor.newInstance0(Native Method)
            at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
            at android.view.LayoutInflater.createView(LayoutInflater.java:854)
            at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1006)
            at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:961)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:659)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:534)
            at me.jfenn.attribouter.adapters.WedgeAdapter.onCreateViewHolder(WedgeAdapter.kt:12)
            at me.jfenn.attribouter.adapters.WedgeAdapter.onCreateViewHolder(WedgeAdapter.kt:8)
            at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:7216)
            at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6347)
            at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6231)
            at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6227)
            at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2330)
            at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1631)
            at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1591)
            at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:668)
            at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4230)
            at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3941)
            at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4499)
            at android.view.View.layout(View.java:21912)
            at android.view.ViewGroup.layout(ViewGroup.java:6260)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
            at android.view.View.layout(View.java:21912)
            at android.view.ViewGroup.layout(ViewGroup.java:6260)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
            at android.view.View.layout(View.java:21912)
            at android.view.ViewGroup.layout(ViewGroup.java:6260)
            at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1695)
            at android.view.View.layout(View.java:21912)
            at android.view.ViewGroup.layout(ViewGroup.java:6260)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
            at android.view.View.layout(View.java:21912)
            at android.view.ViewGroup.layout(ViewGroup.java:6260)
            at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
            at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
            at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
            at android.view.View.layout(View.java:21912)
            at android.view.ViewGroup.layout(ViewGroup.java:6260)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
            at android.view.View.layout(View.java:21912)
            at android.view.ViewGroup.layout(ViewGroup.java:6260)
            at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
            at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
            at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
            at android.view.View.layout(View.java:21912)
    E/AndroidRuntime:     at android.view.ViewGroup.layout(ViewGroup.java:6260)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
            at com.android.internal.policy.DecorView.onLayout(DecorView.java:779)
            at android.view.View.layout(View.java:21912)
            at android.view.ViewGroup.layout(ViewGroup.java:6260)
            at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3080)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2590)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1721)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7598)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:966)
            at android.view.Choreographer.doCallbacks(Choreographer.java:790)
            at android.view.Choreographer.doFrame(Choreographer.java:725)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:951)
            at android.os.Handler.handleCallback(Handler.java:883)
            at android.os.Handler.dispatchMessage(Handler.java:100)
            at android.os.Looper.loop(Looper.java:214)
            at android.app.ActivityThread.main(ActivityThread.java:7356)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
         Caused by: java.lang.UnsupportedOperationException: Failed to resolve attribute at index 2: TypedValue{t=0x2/d=0x7f04003a a=-1}
            at android.content.res.TypedArray.getColorStateList(TypedArray.java:596)
            at androidx.cardview.widget.CardView.<init>(CardView.java:127)
            at com.google.android.material.card.MaterialCardView.<init>(MaterialCardView.java:117)
            at com.google.android.material.card.MaterialCardView.<init>(MaterialCardView.java:113)
            	... 71 more
    

    Usage is like this (in another fragment):

    if (childFragmentManager.findFragmentById(R.id.container_fragment) == null) {
        childFragmentManager.commit {
            add(R.id.container_fragment, Attribouter.from(requireContext()).toFragment())
        }
    }
    
    bug 
    opened by hameno 7
  • FlexBox layout problems

    FlexBox layout problems

    It seems that the button "links" occasionally have problems asserting their dimensions within the FlexBox RecyclerView layouts they're contained in...

    • Occasionally, I've opened the about page to find that all of the links have decided to use the smallest width possible, and the button text has wrapped to multiple lines
    • Most of the time a UserDialog is opened, the links recycler will not display a second line of items regardless of whether they have wrapped to another line, even though its height is wrap_content. Possibly related to google/flexbox-layout#349.
    bug 
    opened by fennifith 5
  • LicensesWedge force title as string resource

    LicensesWedge force title as string resource

    Hi, Is something like this possible with the current implementation? Prefixing it with "^" didn't work.

    <me.jfenn.attribouter.wedges.LicensesWedge
            title="@string/open_source_licences" 
    ...
    
    opened by adrcotfas 5
  • freeze after updating from 0.0.8 to 0.1.0

    freeze after updating from 0.0.8 to 0.1.0

    When updating the library version and opening an Attribouter fragment, I get the following error:

    W/System.err: java.lang.ClassNotFoundException: about W/System.err: at me.jfenn.attribouter.fragments.AboutFragment.onCreateView(AboutFragment.java:65) W/System.err: Caused by: java.lang.ClassNotFoundException: Didn't find class "about" on path: DexPathList[[zip file ... W/System.err: at me.jfenn.attribouter.fragments.AboutFragment.onCreateView(AboutFragment.java:65) W/System.err: at me.jfenn.attribouter.fragments.AboutFragment.onCreateView(AboutFragment.java:90) W/System.err: at me.jfenn.attribouter.fragments.AboutFragment.onCreateView(AboutFragment.java:90) W/System.err: at me.jfenn.attribouter.fragments.AboutFragment.onCreateView(AboutFragment.java:90) W/System.err: at me.jfenn.attribouter.fragments.AboutFragment.onCreateView(AboutFragment.java:90) W/System.err: at me.jfenn.attribouter.fragments.AboutFragment.onCreateView(AboutFragment.java:90) W/System.err: at me.jfenn.attribouter.fragments.AboutFragment.onCreateView(AboutFragment.java:90) W/System.err: at me.jfenn.attribouter.fragments.AboutFragment.onCreateView(AboutFragment.java:90)

    opened by adrcotfas 5
  • Improved data retrieval in GitHubData class

    Improved data retrieval in GitHubData class

    Once GitHubThread is initiated, a new thread is started - responsible for checking if cache is valid. Locks are used to handle the two threads, http call starts running asap after cache thread has finished. Http thread maintains cache property set by the owner - no http connection calls if cache is last modified within the last 10 days. In case http thread runs before cache thread, http thread checks if cache thread is alive - if yes, http thread waits until cache thread successfully finished.

    This should solve the loading lag if the cause was making http requests. Let me know how it goes for you.

    opened by rroyGit 4
  • Custom links on AppInfo have highest priority

    Custom links on AppInfo have highest priority

    When adding a custom link to the AppInfo it will automatically have the highest priority and thus appear on the left side of the list. A work around for this would currently be to just set the priority to -1 but this isn't the cleanest solution.

    opened by nyancrimew 4
  • Support for Twitter profiles or possibly other custom attributes

    Support for Twitter profiles or possibly other custom attributes

    Support for adding twitter handles for users aswell as the project. It might actually be better to implement a way to add custom attributes instead before adding endless new attributes in the library.

    Might work on something like this myself in the evening, will send a PR if it results in anything usable.

    enhancement 
    opened by nyancrimew 4
  • AppWedge description attribute seems to do nothing

    AppWedge description attribute seems to do nothing

    Hey James,

    In me.jfenn.attribouter.wedges.AppWedge#L23 and also in both wikis there is the description attribute referenced, but when I tried to migrate from 0.1.5, it's nowhere used in the new UI:

    Screenshot_20210202_160606

    Is the attribute deprecated or not implemented yet?

    opened by divadsn 2
  • Remove hardcoded prefix word in Licences and Translators wedges when overflow is 0

    Remove hardcoded prefix word in Licences and Translators wedges when overflow is 0

    Hi, Setting a custom title, I would expect to see that string without a prefix in front of it.

    <me.jfenn.attribouter.wedges.LicensesWedge
        overflow="0"
        title="Open Source Licences"
        showDefaults="false">
        <me.jfenn.attribouter.wedges.LicenseWedge repo="TheAndroidMaster/Attribouter" />
    </me.jfenn.attribouter.wedges.LicensesWedge>
    

    will produce "View Open Source Licences". Removing the overflow="0" will produce the desired string of "Open Source Licences".

    enhancement 
    opened by adrcotfas 2
  • ItemData instance creation from XML

    ItemData instance creation from XML

    This will require extensive modifications to the CLI (or even just replacing it completely - see #36), but I essentially want the XML to be "inflated" similarly to a layout file, where the entire package name of each class is provided in the tag. Sub-tags, like "contributor" or "link" would be unaffected (or even replaced with an ambiguous name, like "item"), and would simply be passed to the constructor of each "main" item (or I could just continue passing an instance of the parser like I am now). This would allow users to add their own functionality to the about screen instead of only being able to override layout files.

    enhancement 
    opened by fennifith 2
  • Update Config at Build Time

    Update Config at Build Time

    Might need to write an extra module to do this, but it's worth it. Also, an option to disable all runtime network requests should be present if this is implemented.

    enhancement 
    opened by fennifith 0
  • Replace CLI with a website

    Replace CLI with a website

    It'd be more accessible to users that aren't as familiar with the command line, would probably solve a lot of the existing formatting issues, and could also serve to document the library a little better than just a single README (related: #14).

    enhancement 
    opened by fennifith 1
  • Better String Resource / Translation support

    Better String Resource / Translation support

    Currently, unless otherwise specified, all data specified in an app's config file is replaced with the versions of that data fetched from github unless otherwise specified. However, the github versions of that data cannot be translated (as I'm pretty sure the github api only provides information in one language at a time, and most people do not write their bios in more than one language). Ideally, there would be a way to only fetch data from github for one language, and use the translated strings for the rest. This would require some extremely weird syntax in the config file though. Maybe something like bio="^(lang != en)@strings/someone_bio" to specify that the string should only be overwritten if the current locale is set to en. The lang != part isn't necessarily needed, but it improves readability and I might need to use the same or similar syntax elsewhere in this project later on.

    enhancement 
    opened by fennifith 0
  • CrowdIn Translations Support

    CrowdIn Translations Support

    I will probably not be working on this anytime soon, as their API looks very painful to use and doesn't provide profile pictures. @deletescape wrote a python script to fetch & convert them to xml here that might come in handy if I (or someone else) want to implement this in the future, though.

    enhancement 
    opened by fennifith 0
Releases(0.1.9)
  • 0.1.9(Mar 17, 2021)

  • 0.1.8(Jun 27, 2020)

  • 0.1.7(Jun 26, 2020)

    • converted more of the library to Kotlin, including the external APIs
    • replaced internal data models with those from the multiplatform git-rest-wrapper project for future interop & maintainability
    • implement full support for GitLab and Gitea (#22)
    • fixed a couple backwards compatibility issues & behavior

    Migration notes:

    • The "default" behavior of each user/repository identifier is set to query GitHub - however, this is not guaranteed to remain constant in the future. Any IDs should be changed to the new format: github:<id> or gitlab:<id> or [email protected]:<id>, etc. The full specification for this format can be found here.
    • Several XML properties have been changed. Existing properties will not cause an error, but will cease to have their desired effect.
      • ContributorWedge.blog, LicenseWedge.website, and TranslatorWedge.blog have been renamed to websiteUrl for consistency
      • LicenseWedge.repoUrl can now be used to specify the URL of a repository; however, it will still be supplemented by the URL inferred from LicenseWedge.repo by default. AppWedge.repoUrl and ContributorWedge.profileUrl achieve the same purpose.
    • The old API format is still valid. However, there is now a more concise Kotlin DSL if you wish to switch to it.
    Source code(tar.gz)
    Source code(zip)
    attribouter.apk(4.53 MB)
  • 0.1.6(Apr 6, 2020)

    • converted a good portion of the library to Kotlin
    • implemented Retrofit for better network/request handling
    • finished much of the refactoring/groundwork for #22 and similar "service-independent" functionality
    • added a fallback behavior for invalid auth tokens (#65)
    • created avatar placeholders for contributors/translators (#63)
    • prevent crashes when the app name/label is unspecified (#60)
    • wrote a set of proguard rules to prevent issues with minifyEnabled (#66)

    Migration notes:

    Specifying the full package/class name of a wedge in the configuration file is now optional; <me.jfenn.attribouter.wedges.AppWedge> can be renamed to just <AppWedge>. Also, all occurrences of <LinkWedge> must be changed, as they have been moved out of their .link sub-package/folder/thing into the parent .wedges package with all the other wedge-like classes.

    Source code(tar.gz)
    Source code(zip)
    Attribouter.apk(4.59 MB)
  • 0.1.5(Nov 9, 2018)

  • v0.1.4(Sep 29, 2018)

  • v0.1.3(Sep 18, 2018)

  • v0.1.2(Aug 29, 2018)

  • v0.1.1(Aug 20, 2018)

  • v0.1.0(Aug 10, 2018)

    • "InfoData" classes are called "Wedges" now
    • "wedges" now require a full class name in the config file
    • you can now create custom "wedges" to add to the config file: #37
    • the CLI for updating the configuration file is useless and out of date, it has been removed: #36
    • support for theming dialogs: #41
    • ability to remove AppWedge's description: #40
    • fixed LicenseWedge's overflow title string thing: #39

    !!! THIS IS AN IMPORTANT THING !!!

    If you are updating from a previous version, you will need to make a few changes to your configuration file. All of the tags in the file should be replaced with the full class name of the object that they instantiate. A full list of all of the tags and the class names to replace them with is below:

    • appInfo -> me.jfenn.attribouter.wedges.AppWedge
    • contributors -> me.jfenn.attribouter.wedges.ContributorsWedge
    • contributor -> me.jfenn.attribouter.wedges.ContributorWedge
    • translators -> me.jfenn.attribouter.wedges.TranslatorsWedge
    • translator -> me.jfenn.attribouter.wedges.TranslatorWedge
    • licenses -> me.jfenn.attribouter.wedges.LicensesWedge
    • project -> me.jfenn.attribouter.wedges.LicenseWedge
    • link -> me.jfenn.attribouter.wedges.link.LinkWedge
    • text -> me.jfenn.attribouter.wedges.TextWedge
    Source code(tar.gz)
    Source code(zip)
    Attribouter.apk(2.50 MB)
  • v0.0.8(Jul 17, 2018)

  • v0.0.7(May 13, 2018)

  • v0.0.6(May 7, 2018)

  • v0.0.5(May 6, 2018)

  • v0.0.4(Apr 28, 2018)

    • bug fixes for pre-lollipop devices
    • new "translators" section - #9
    • customizable "links" for appInfo, contributor, and project tags - #12
    • "overflow" attribute for displaying a set amount of items in contributor, translator, and license lists - #10
    • ability to remove default contributors/licenses - #13
    Source code(tar.gz)
    Source code(zip)
    Attribouter.apk(2.49 MB)
  • v0.0.2(Mar 16, 2018)

Owner
James Fenn
Enjoys writing software on loud keyboards. Starts too many projects. Consumes food.
James Fenn
Techbee e.U. 62 Jan 31, 2023
LocationPhoneMode - The app will ask the user to allow it to track the user location

LocationPhoneMode The app will ask the user to allow it to track the user locati

null 1 Jan 11, 2022
Movie Info - MovieInfo app that recieves popular movies and allow the user to search for specific movie through the restapi

Movie_Info MovieInfo app that recieves popular movies and allow the user to sear

inderjeet yadav 3 Jun 8, 2022
Asteroid Radar is an app that allow you to view the asteroids detected by NASA that pass near Earth

Asteroid-radar Asteroid Radar is an app that allow you to view the asteroids detected by NASA that pass near Earth. you can view all the detected aste

Ana Stanescu 5 Aug 6, 2022
Android-splash-screen-demo - Sample application to demo the various features provided in android-splash-screen

Android Splash screen API demo This is a sample application used to demonstrate the various features provided in android-splash-screen. More details c

Sridhar 1 Jan 3, 2022
Quick start with the Google Maps Android API

Please note: This repository is not currently maintained, and is kept for historical purpose only. You can find an up to date tutorial here: https://g

Google Archive 234 Nov 14, 2022
A simple textfield for adding quick notes without ads.

Simple Notes A simple textfield for adding quick notes. Need to take a quick note of something to buy, an address, or a startup idea? Then this is the

Simple Mobile Tools 670 Dec 31, 2022
A calculator for quick simple calculations with a nice user interface and no ads

Simple Calculator A calculator with the basic functions and a customizable widget. You can copy the result or formula to clipboard by long pressing it

Simple Mobile Tools 482 Dec 31, 2022
Easy and quick contact management with no ads, handles groups and favorites too.

Simple Contacts A simple app for creating or managing your contacts from any source. The contacts can be stored on your device only, but also synchron

Simple Mobile Tools 621 Dec 26, 2022
ImmutablePendingIntent provides mutability safe methods and quick fix

ImmutablePendingIntent Overview ImmutablePendingIntent provides methods that create immutability-safe PendingIntent lint to make an error when using m

wada811 5 Nov 25, 2022
A work-in-progress quiz app I started developing for a client but got paused.

quiz-app A work-in-progress quiz app I started developing for a client but got paused. Background This app was intended to be a trivia app where users

Ahmet Safa Orhan 7 Oct 18, 2022
Yet another ToDo app, but the UI completely written in Jetpack Compose!

Yet another ToDo app, but the UI completely written in Jetpack Compose!

Wisnu Kurniawan 134 Dec 31, 2022
A blogging mobile application built with Kotlin using MVC design pattern and Take some advantage of Jetpack , View & Data Binding It's a mimic for Tumblr application , But a little prettier than him 😉

A blogging mobile application built with Kotlin using MVC design pattern and Take some advantage of Jetpack , View & Data Binding It's a mimic for Tum

Ahmed Ihab 17 Dec 23, 2022
A minimalist but powerful productivity timer designed to keep you focused and free of distractions.

Goodtime A minimalist but powerful productivity timer designed to keep you focused and free of distractions. Alternate between focused work sessions a

Adrian Cotfas 692 Dec 27, 2022
Theme for VSCode and JetBrains IDEs, based on morhetz/gruvbox but with a plainer color palette for syntax highlighting.

gruvbox-plain Theme for VSCode and JetBrains IDEs, based on morhetz/gruvbox but with a plainer color palette for syntax highlighting. Syntax Colors gr

null 2 Dec 28, 2022
Social media app but with new features like translate messages and posts , support video & images as posts , chat and notifications , etc...

social-media-app Social media app but with new features like translate messages and posts , support video & images as posts , chat and notifications ,

Kareem Aboelatta 15 Dec 13, 2022
akka-samples-peristence-java by Lightbend, but using Kotlin.

This example illustrates event sourcing with Akka Persistence. it is based (okay, stolen from) the generated sample project akka-samples-peristence-ja

Jurjen Vorhauer 1 Oct 11, 2022
Android cutout screen support Android P. Android O support huawei, xiaomi, oppo and vivo.

CutoutScreenSupport Android cutout screen support Android P. Android O support huawei, xiaomi, oppo and vivo. Usage whether the mobile phone is cutout

hacket 5 Nov 3, 2022
Android samples built using Jetpack Window Manager for foldable and dual-screen devices like Microsoft Surface Duo.

Jetpack Window Manager samples for dual-screen and foldable devices like Microsoft Surface Duo Android app samples that use Jetpack Window Manager to

Microsoft 45 Dec 19, 2022