The ribot studio app for the Android Platform

Overview

Thank you for your interest in ribot’s development work. Unfortunately there are no current plans to make any changes to this project in the near future, and it is not being maintained or updated. We hope you still find this a useful resource.

ribot app Android

The official ribot app for android. It follows the architecture, tools and guidelines that we use when developing for the Android platform (https://github.com/ribot/android-guidelines)

Screenshots

The ribot app is an internal project based off of our Android Boilerplate. It was created to make us aware of our fellow ribots locations. Currently, the app allows you to:

  • Sign in - Sign in using your @ribot.co.uk Google account
  • Auto Check-in - Using Estimote beacons, the app will automatically check you in at your corresponding location in the ribot studio
  • Manual Check-in - Manually check yourself in at your current location
  • Team listing - View a list of ribots and their current check-in status

We've open sourced this to both showcase our efforts and allow you to recreate the experience in your own workplace - why not fork this project and give it a go?

Libraries

The libraries and tools used include:

Requirements

Build Instructions

In order to run this project, you'll need to setup several things beforehand:

  • Our application uses the ribot API to handle data requests, you'll need to clone this and configure it to your requirements. This application will not work without an API and the implementation in this repository requires the use of an @ribot.co.uk email address, so your own API is required if you wish to clone this project.

  • You'll need to set the values found in the gradle.properties file. This involves the web application client id (ribotAppGoogleApiServerClientId) and debug/release keystore locations, aliases and passwords. Ideally the UNDEFINED values found in this file should be defined in the Global gradle.properties that lives in ~/.gradle/gradle.properties

Architecture

This project follows our Android architecture guidelines. Read more about them here.

Code Quality

This project integrates a combination of unit tests, functional test and code analysis tools.

Tests

To run unit tests on your machine:

./gradlew test

To run functional tests on connected devices:

./gradlew connectedAndroidTest

Note: For Android Studio to use syntax highlighting for Automated tests and Unit tests you must switch the Build Variant to the desired mode.

Code Analysis tools

The following code analysis tools are set up on this project:

  • PMD: It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and so forth. See this project's PMD ruleset.
./gradlew pmd
  • Findbugs: This tool uses static analysis to find bugs in Java code. Unlike PMD, it uses compiled Java bytecode instead of source code.
./gradlew findbugs
./gradlew checkstyle

The check task

To ensure that your code is valid and stable use check:

./gradlew check

This will run all the code analysis tools and unit tests in the following order:

Check Diagram

Licence

Copyright 2015 Ribot Ltd.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • How do I handle HTTP errors like 401, 403, 503 etc

    How do I handle HTTP errors like 401, 403, 503 etc

    First of all thanks for sharing this awesome project. It's been a great learning resource for me esp Rx and Dagger2 parts. I have learnt a lot form this project and also from Ribots mediums post.

    I am trying to figure out how do I handle 401, 403 errors gracefuly

    my Retrofit service is as below

    @POST("user/login")
        Observable<Response<UserSignIn>> signIn(@Body Register signIn);
    

    and my DataManager is as below

    public Observable<Response<UserSignIn>> signIn(String emailId, String password) {
            return mService.signIn(new SatService.Register(emailId, password))
                    .doOnNext(new Action1<Response<UserSignIn>>() {
                        @Override
                        public void call(Response<UserSignIn> response) {
    
                            //TODO: remove sensitive debug logs
                            Timber.d("status code: %s", response.code());
                            Timber.d("body: %s", response.body());
                            Timber.d("error body: %s", response.errorBody());
                            Timber.d("message: %s", response.message());
    
                            if(response.code() == 200) {
                                UserSignIn userSignIn = response.body();
    
                                mPreferencesHelper.putUserId(userSignIn.getUserId());
                                mPreferencesHelper.putAccessToken(userSignIn.getToken());
                                mPreferencesHelper.putExpires(userSignIn.getExpires());
                            } else if (responce.code == 403) {
                                // Handle this error gracefully and return some meaningful text which I can show it to the user on screen
    
                            } ...
                        }
                    });
        }
    

    As you can see I am returning Observable<Response<UserSignIn>> from DataManager to my Presenter and in my Presenter I am again doing if(response.code() == 200) { } which is redundant and feels wrong to me.

    I think I should not pass Response to Presenter but should pass Observable

    I thought of using map and flatMap, but again I am stuck up with what should go in else part?

    mService.signIn(new SatService.Register(emailId, password))
                .map(new Func1<Response<UserSignIn>, UserSignIn>() {
                @Override
                public UserSignIn call(Response<UserSignIn> response) {
    
                    if(response.code() == 200) {
                        return response.body();
                    } else {
                       ** return ;** // what should I return here?
                    }
                }
            });
    

    Is there a better way to Handle this? Is this the case for EventBus?

    opened by rinav 4
  • Subcomponent & Dependency between ActivityComponent & ApplicationComponent

    Subcomponent & Dependency between ActivityComponent & ApplicationComponent

    Hi, I have been checking the code for "android-boilerplate" as template for new project, and this project "ribot-app-android" as sample implementation utilizing same project.

    I really admire all the work done in "android-boilerplate" & I consider one of the best (If not the best) samples of complete typical Android application.

    I have a question regarding these two projects trying to understand:

    • In "ribot-app-android" there is dependency between ActivityComponent & ApplicationComponent but in "android-boilerplate" I don't find such relation but I find Subcomponent definition. May I know why?

    Thanks in advance.

    opened by ashraffouad 3
  • FindBugs   File not found: filesystem:app/build/intermediates/classes

    FindBugs File not found: filesystem:app/build/intermediates/classes

    I am trying to implement the Findbugs in my project but It's not passing findbugs check. Here is the error log

    The following errors occurred during analysis:
      File not found: filesystem:app/build/intermediates/classes
    :app:findbugs FAILED
    FAILURE: Build failed with an exception.
    * What went wrong:
    Execution failed for task ':app:findbugs'.
    > java.io.IOException: No files to analyze could be opened
    * Try:
    Run with --info or --debug option to get more log output.
    * Exception is:
    org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:findbugs'.
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:84)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:55)
    	at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:62)
    	at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
    	at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:88)
    	at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:46)
    	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:51)
    	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
    	at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
    	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.execute(DefaultTaskGraphExecuter.java:236)
    	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.execute(DefaultTaskGraphExecuter.java:228)
    	at org.gradle.internal.Transformers$4.transform(Transformers.java:169)
    	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:106)
    	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:61)
    	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:228)
    	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:215)
    	at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:77)
    	at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:58)
    	at org.gradle.execution.taskgraph.ParallelTaskPlanExecutor.process(ParallelTaskPlanExecutor.java:50)
    	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:113)
    	at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:37)
    	at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
    	at org.gradle.execution.DefaultBuildExecuter.access$000(DefaultBuildExecuter.java:23)
    	at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:43)
    	at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
    	at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
    	at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:30)
    	at org.gradle.initialization.DefaultGradleLauncher$3.execute(DefaultGradleLauncher.java:196)
    	at org.gradle.initialization.DefaultGradleLauncher$3.execute(DefaultGradleLauncher.java:193)
    	at org.gradle.internal.Transformers$4.transform(Transformers.java:169)
    	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:106)
    	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:56)
    	at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:193)
    	at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:119)
    	at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:102)
    	at org.gradle.launcher.exec.GradleBuildController.run(GradleBuildController.java:71)
    	at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:28)
    	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:41)
    	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
    	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:75)
    	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:49)
    	at org.gradle.tooling.internal.provider.ServicesSetupBuildActionExecuter.execute(ServicesSetupBuildActionExecuter.java:44)
    	at org.gradle.tooling.internal.provider.ServicesSetupBuildActionExecuter.execute(ServicesSetupBuildActionExecuter.java:29)
    	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:67)
    	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:47)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
    	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
    	at org.gradle.util.Swapper.swap(Swapper.java:38)
    	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    	at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:60)
    	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:72)
    	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
    	at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
    	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
    	at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
    Caused by: org.gradle.api.UncheckedIOException: java.io.IOException: No files to analyze could be opened
    	at org.gradle.internal.UncheckedException.throwAsUncheckedException(UncheckedException.java:43)
    	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:76)
    	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.doExecute(DefaultTaskClassInfoStore.java:141)
    	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:134)
    	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:123)
    	at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:632)
    	at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:615)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:95)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:76)
    	... 70 more
    Caused by: java.io.IOException: No files to analyze could be opened
    	at edu.umd.cs.findbugs.FindBugs2.execute(FindBugs2.java:276)
    	at org.gradle.api.plugins.quality.internal.findbugs.FindBugsExecuter.runFindbugs(FindBugsExecuter.java:39)
    	at org.gradle.process.internal.worker.request.WorkerAction.run(WorkerAction.java:87)
    	at org.gradle.process.internal.worker.request.WorkerAction.runThenStop(WorkerAction.java:71)
    	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    	at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:377)
    	... 2 more
    BUILD FAILED
    
    opened by therajanmaurya 2
  • Is it necessary to use

    Is it necessary to use

    What is the use of this line if (subscriber.isUnsubscribed()) return;

    in below code.

      public Observable<Ribot> setRibots(final Collection<Ribot> newRibots) {
            return Observable.create(new Observable.OnSubscribe<Ribot>() {
                @Override
                public void call(Subscriber<? super Ribot> subscriber) {
                    if (subscriber.isUnsubscribed()) return;
                    BriteDatabase.Transaction transaction = mDb.newTransaction();
                    try {
                        mDb.delete(Db.RibotProfileTable.TABLE_NAME, null);
                        for (Ribot ribot : newRibots) {
                            long result = mDb.insert(Db.RibotProfileTable.TABLE_NAME,
                                    Db.RibotProfileTable.toContentValues(ribot.profile()),
                                    SQLiteDatabase.CONFLICT_REPLACE);
                            if (result >= 0) subscriber.onNext(ribot);
                        }
                        transaction.markSuccessful();
                        subscriber.onCompleted();
                    } finally {
                        transaction.end();
                    }
                }
            });
        }
    

    Is it necessary to use it ??

    opened by sagar15795 2
  • Subscription not working

    Subscription not working

    Hi, I am little confused about the subscription.

      if (mSubscription != null) mSubscription.unsubscribe();
    

    In my case, I am making two retrofit calls one by one.

     @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragmen, null);
    
        loadClientTemplate();
        loadOffices();
        retrun rootView;
    }
    

    My Presenter Methods are

    public void loadClientTemplate() {
    checkViewAttached();
            getMvpView().showProgressbar(true);
            mSubscription = mDataManager.getClientTemplate()
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribeOn(Schedulers.io())
                    .subscribe(new Subscriber<ClientsTemplate>() {
                        @Override
                        public void onCompleted() {
                            getMvpView().showProgressbar(false);
                        }
    
                        @Override
                        public void onError(Throwable e) {
                            getMvpView().showProgressbar(false);
                            getMvpView().showFetchingError("Failed to fetch clientTemplate");
                        }
    
                        @Override
                        public void onNext(ClientsTemplate clientsTemplate) {
                            getMvpView().showProgressbar(false);
                            getMvpView().showClientTemplate(clientsTemplate);
                        }
                    });
        }
    
    public void loadOffices() {
            checkViewAttached();
            getMvpView().showProgressbar(true);
            mSubscription = mDataManager.getOffices()
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribeOn(Schedulers.io())
                    .subscribe(new Subscriber<List<Office>>() {
                        @Override
                        public void onCompleted() {
                            getMvpView().showProgressbar(false);
                        }
    
                        @Override
                        public void onError(Throwable e) {
                            getMvpView().showProgressbar(false);
                            getMvpView().showFetchingError("Failed to load offices list");
                        }
    
                        @Override
                        public void onNext(List<Office> offices) {
                            getMvpView().showProgressbar(false);
                            getMvpView().showOffices(offices);
                        }
                    });
        }
    

    Yes, I understand the logic for unsubscribing the Subscription. As first call is completed and when next call will come unsubscribe the subscription and subscribe to subscription again.

    but here I am making both calls simultaneously, So I can not use.

      if (mSubscription != null) mSubscription.unsubscribe();
    

    At last here, subscription will have two Observables.

    can I have to unsubscribe all, at last of the calls completions ?

    opened by therajanmaurya 2
  • Implement Refresh access token logic

    Implement Refresh access token logic

    Hi, I am doing my GSoC project https://github.com/therajanmaurya/android-client-2.0 in which I am using RxJava 2 and handling rest API calls. Initially, a user logged in and get an access token, I don’t know when this access token will expire so I want to implement a logic that will call, whenever I will get 401 error, that should fire a request to the server to refresh the access token and try the current request again. I don’t want check 401 in every request-response. I want to implement this in such a way that I don’t need to check 401 response code in every rest API call, if any request code is 401 then it will automatically make another request to refresh the access token and try the current request again.

    opened by therajanmaurya 1
  • Updates to Libraries

    Updates to Libraries

    Hey Thanks for a great app and architecture.

    Do you plan to make updates to the boilerplate project ? (like RxJava, BuildTools, Google Services , etc) ?

    Thanks.

    opened by jazzyjester 1
  • App rotation on request

    App rotation on request

    Hi ! Just curious: Is it right that the app somehow forget that it sent a sign-in request if the user turn the screen of the device after pushing the button ? The http request get cancelled and loading sign get removed.

    How is your way to handle this when the current state (loading) can barely get stored in DB ?

    question 
    opened by legarspol 1
  • update to retrofit 2.0.0-beta3

    update to retrofit 2.0.0-beta3

    ah, now i see that looks messy because I didn't have my environment with the keys and didn't want to configure it. But you can do it manually if you want.

    1. Modify your gradle file to

      compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3' compile "com.squareup.retrofit2:converter-gson:2.0.0-beta3" compile "com.squareup.retrofit2:adapter-rxjava:2.0.0-beta3" compile 'com.squareup.okhttp3:logging-interceptor:3.0.1'

    create a builder

      OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
      builder.readTimeout(10, TimeUnit.SECONDS);
      builder.connectTimeout(5, TimeUnit.SECONDS);
    

    and add interceptors to it.

    this is all :)

    opened by AndreiD 1
  • Using @ActivityContext Context context in Presenter is good or not

    Using @ActivityContext Context context in Presenter is good or not

    In My project, I have managed all strings in strings.xml and thinking to add also Presenter String message into strings.xml but I think Presenter does deal with Context.

    Is it good to use Context in Presenter.

    opened by therajanmaurya 0
  • Using Observable.create without checking for isSubscribed and without an obvious reason

    Using Observable.create without checking for isSubscribed and without an obvious reason

    I have noticed that throughout the whole project and as an example in the PreferencesHelper.class you are using the Observable.create method without checking for isSubscribed. It is maybe not a problem in your exact usage pattern but as suggested from reowned Android Devs the usage of Observable.create should be avoided for various reasons. I think you can replace the Observable.create in most places with either Observable.just or Observable.fromCallable. There you get most of the thinks required from the Observable contract for free.

    opened by akoufa 2
  • Do we have a better implementation for DataManager?

    Do we have a better implementation for DataManager?

    As this question Implementing MVP in Android app described, DataManager is a big concern if I am gonna take the architecture applied in ribot.

    It's a simple fact that DataManager will grow as the business grows. The ribot android app is just a small demo project and we already have a medium size DataManager here. How would you deal with the problem if the DataManager grows too big?

    opened by ryanhoo 4
The Google I/O Android App

Google I/O Android App 2021 Update Due to global events, Google I/O 2020 was canceled and Google I/O 2021 is an online-only event, so the companion ap

Google 21.7k Jan 7, 2023
PocketHub Android App

PocketHub This repository contains the source code for the PocketHub Android app. This is the same repository as the now-defunct official GitHub Andro

null 9.4k Dec 27, 2022
Skooter App - Android client source code

Skooter App STAY CONNECTED. LOCALLY Skooter is a FREE mobile application that acts like a Hyper Local Bulletin Board for your area by showing the most

Prathmesh Ranaut 61 Feb 23, 2022
Material Design ready and feature rich Twitter/Mastodon/Fanfou app for Android 4.1+.

Twidere for Android Material Design ready and feature rich Twitter/Mastodon/Fanfou app for Android 4.1+. Enjoy Fediverse now! Twidere-Android is maint

Twidere Project 2.7k Jan 1, 2023
An Android App for Github

A Github Android APP How does it look like ? Feature Last Events Repository Detail User Detail Read formatted code Star repository, follow people Sear

Quinn 492 Jan 3, 2023
YouJoin Android Client, a cool app of connection, use Material Design.

YouJoin-Android 简介 这个repo是YouJoin社交平台的Android客户端,代码由本人独立编写,功能和特点包括: 采用Material Design设计 登录注册 即时聊天 个人中心(支持资料编辑、头像上传) 心情动态(支持最多九张图片;支持点赞、评论) 好友关注 附近的人(采

ZZQ 298 Nov 29, 2022
A app client for Github

Git.NB A android app client for Github. 效果图 关于项目 Git.NB现以升级到2.0,功能大有升级,UI大改版本。各种有趣的操作,欢迎下载代码试完。 如果发现有任何问题和建议,随时欢迎Email或者开Issues, 如果有版权问题,立即删除。 使用的开源技术

null 319 Sep 9, 2022
A clone of the TikTok App using Flutter.

Tik Tok App clone TikTok is an iOS and Android media app for creating and sharing short videos. The app was launched in 2017 by ByteDance, for markets

Maxwell Cofie 211 Dec 19, 2022
Share twitter url to this app, and you will be redirected.

twitter2nitter - redirect twitter to nitter Share twitter url to this app, and you will be redirected. Redirect works for: Open twitter url with twitt

null 14 Dec 20, 2022
Android Stories library - Instagram-like android stories library that supports images from disk or from internet (url)

Android Stories Library Instagram like stories library for Android. Add it in your root build.gradle at the end of repositories: allprojects { reposi

Panagiotis Makris 3 Dec 20, 2022
Library for easy work with Facebook, Twitter, LinkedIn and Google on Android

THIS PROJECT IS NO LONGER MAINTAINED, FEEL FREE TO FORK AND FIX IT FOR YOUR NEEDS There is also an Android Library that is being maintained, CloudRail

Anton Krasov 1k Dec 18, 2022
Easy social network authorization for Android. Supports Facebook, Twitter, Instagram, Google+, Vkontakte. Made by Stfalcon

SocialAuthHelper A library that helps to implement social network authorization (Facebook, Twitter, Instagram, GooglePlus, Vkontakte). Who we are Need

Stfalcon LLC 97 Nov 24, 2022
Youtube-dl for android

youtube-dl-android Android library wrapper for youtube-dl executable. Based on yausername's youtubedl-android but with ability to download binary file

Vikas 97 Dec 30, 2022
Twidere for Android

Twidere for Android Material Design ready and feature rich Twitter/Mastodon/Fanfou app for Android 4.1+. Enjoy Fediverse now! Twidere-Android is maint

Twidere Project 2.7k Dec 27, 2022
A Reddit client for Android

This is a Reddit client on Android written in Java. It does not have any ads and it features clean UI and smooth browsing experience.

null 2.6k Jan 9, 2023
An unofficial open source Reddit client for Android.

RedReader An unofficial, open source Android client for Reddit. Features Free and open-source Software - no ads/tracking Lightweight and fast Swipe po

null 1.4k Jan 7, 2023
Slide is an open sourced, ad free Reddit browser for Android

Slide Slide is an open source, ad free Reddit browser for Android. It is based around the Java Reddit API Wrapper. Slide is available on the Google Pl

Carlos Crane 1.7k Dec 27, 2022
Open-source alternative Instagram client on Android.

Instagram client; previously known as InstaGrabber.

Austin Huang 1.1k Jul 23, 2021
Kickstarter for Android. Bring new ideas to life, anywhere.

Welcome to Kickstarter's open source Android app! Come on in, take your shoes off, stay a while—explore how Kickstarter's native squad has built and c

Kickstarter 5.7k Jan 2, 2023