Annotation based simple API flavored with AOP to handle new Android runtime permission model

Overview

Let

Android Arsenal Join the chat at https://gitter.im/canelmas/let

Annotation based simple API flavoured with AOP to handle new Android runtime permission model.

If you check Google's Samples about the new permission model, you'll see a lot of boiler plate code for requesting, handling and retrying the request for required permissions.

Let will minimize the boiler plate code you have to write for requesting and handling permissions and hence help you keep your code more readable.

Let let Handle

Annotate your methods requiring permissions with @AskPermission and let Let handle the rest.

@AskPermission(ACCESS_FINE_LOCATION)
private void getUserLocationAndDoSomething() {
    Toast.makeText(
        SampleActivity.this, 
        "Now that I have the permission I need, I'll get your location and do something with it", 
        Toast.LENGTH_SHORT
    ).show();
    ...
}
@AskPermission({
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.CALL_PHONE,
            Manifest.permission.CAMERA
})
private void skipTutorial() {
    // permissions needed for the best app experience are granted; let's go to the app's home screen
    startActivity(new Intent(this, HomeActivity.class));
}

Let will check these annotated methods and execute them unless the permissions required are granted; otherwise Let will put on hold the method execution and request these permissions at runtime. After examining the permission request result, Let will execute the method already put on hold only if the permissions are granted by user.

Let will also inform about the rationales before making any permission request and tell about denied permissions; whether they're simply denied or with 'Never Ask Again' checked.

Just make sure to override the onRequestPermissionsResult in your Activity or Fragment, where your @AskPermission annotated methods are located:

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    Let.handle(this, requestCode, permissions, grantResults);
}

And make sure your Activity or Fragment implements RuntimePermissionListener in order to get notified about denied permissions and rationales:

public class SampleActivity extends AppCompatActivity implements RuntimePermissionListener {
    
    // ....
    
    @Override
    public void onShowPermissionRationale(List<String> permissions, final RuntimePermissionRequest request) {
        /**
        * you may show permission rationales in a dialog, wait for user confirmation and retry the permission 
        * request by calling request.retry()    
        */               
    }
  
    @Override
    public void onPermissionDenied(List<DeniedPermission> deniedPermissionList) {
        /**
        * Do whatever you need to do about denied permissions:
        *   - update UI
        *   - if permission is denied with 'Never Ask Again', prompt a dialog to tell user
        *   to go to the app settings screen in order to grant again the permission denied 
        */              
    }
    
    //  ...
}

Usage

Add it to your project today!

buildscript {
    repositories {                    
        jcenter()        
    }

    dependencies {        
        classpath 'com.canelmas.let:let-plugin:0.1.11'
    }
}

apply plugin: 'com.android.application'
apply plugin: 'let'

repositories {        
    jcenter()
}

For kotlin :

buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.canelmas.let:let-plugin:1.0.0-beta1'
    }
}

apply plugin: 'com.android.application'
apply plugin: 'let'

repositories {
    jcenter()
}

Proguard

Make sure your proguard rule set includes following lines:

-keep class com.canelmas.let.** { *; }
-keepnames class * implements com.canelmas.let.RuntimePermissionListener

-keepclassmembers class * implements com.canelmas.let.RuntimePermissionListener {
    public void onRequestPermissionsResult(***);
}

-keepclasseswithmembernames class * {
    @com.canelmas.let.* <methods>;
}

License

Copyright 2016 Can Elmas

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
  • java.lang.VerifyError: ActivityWelcome$AjcClosure1

    java.lang.VerifyError: ActivityWelcome$AjcClosure1

    Ever since adding the AskPermissions annotations I've been getting the VerifyError below:

    java.lang.VerifyError: ActivityWelcome$AjcClosure1

    This is honestly my first time seeing an error of this sort so I'll keep digging to see if there's a way to resolve on my own. Posting here just incase others experience a similar issue or have suggestions for resolving.

      @AskPermission(Manifest.permission.ACCESS_FINE_LOCATION)
      private void startTrackingLocation() {
        mTracker = LocationTracker.getInstance(this);
        mTracker.startTracking();
      }
    

    EDIT

    Forgot to mention this is happening on android 4.4.1 and 4.4.2 from what I can tell so far.

    investigating 
    opened by jachenry 13
  • Let annotation crashes on Android N devices

    Let annotation crashes on Android N devices

    Hey there, Thought you'd like to know about this crash on Android N:

    java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.String[] com.canelmas.let.AskPermission.value()' on a null object reference
        at com.canelmas.let.RuntimePermissionRequest.proceed(RuntimePermissionRequest.java:61)
        at com.canelmas.let.RuntimePermissionRequest.proceed(RuntimePermissionRequest.java:52)
        at com.canelmas.let.LetAspect.ajc$inlineAccessMethod$com_canelmas_let_LetAspect$com_canelmas_let_RuntimePermissionRequest$proceed(LetAspect.java:1)
        at com.canelmas.let.LetAspect.annotatedMethods(LetAspect.java:57)
    

    Here's the line of code where it crashed: @AskPermission({ Manifest.permission.ACCESS_FINE_LOCATION })

    Just thought I'd give you a heads up for when the full OS release actually comes out. Love the library!

    bug odd.. 
    opened by olsontl 6
  • Request a permission causes a crash in Android O

    Request a permission causes a crash in Android O

    According to some crash reports I'm receiving, there is an issue happening in Android O when a method annotated with @AskPermission is called.

    This is the crash stacktrace:

    Fatal Exception: com.canelmas.let.LetException: Proceeding with the annotated method failed!
           at com.canelmas.let.RuntimePermissionRequest.proceed(RuntimePermissionRequest.java:139)
           at com.canelmas.let.RuntimePermissionRequest.proceed(RuntimePermissionRequest.java:52)
           at com.canelmas.let.LetAspect.ajc$inlineAccessMethod$com_canelmas_let_LetAspect$com_canelmas_let_RuntimePermissionRequest$proceed(LetAspect.java:1)
           at com.canelmas.let.LetAspect.annotatedMethods(LetAspect.java:57)
    

    EDIT: This happens only if the permissions is granted. Looks like the crash happens when the library tries to invoke the method.

    investigating 
    opened by waninkoko 3
  • onPermissionDenied() never called

    onPermissionDenied() never called

    I have @AskPermission in my fragment that implemented RuntimePermissionListener, something like this:

    @AskPermission({CAMERA, WRITE_EXTERNAL_STORAGE}) private void startCamera() {
            //Display camera view
    }
    

    It works when the permissions got granted, but when I tried to handle denied situation, onPermissionDenied() never gets called . Just wonder when it calls onPermissionDenied()?

    opened by liutingdu 2
  • Let will only execute the annotated method the second time it's called

    Let will only execute the annotated method the second time it's called

    I'm having this problem with that the annotated method is only executed the second time it's called.
    What I mean is that upon the first call to the annotated method the permission dialog is shown correctly but after tapping on Allow the actual method is not executed. I have to rerun the activity for it to actually run.

    opened by 2hamed 1
  • Method call is not forwarded after granting permission

    Method call is not forwarded after granting permission

    Running 6.0.1 on a Nexus 6, I noticed that my methods were not called right after granting permissions. After a few debug, I can clearly see that the request code given to onRequestPermissionsResult() has an offset of 256 compared to the task number in DelayedTasks - resulting in a "No delayed task to execute.

    It looks like requestPermissions() is adding 256 to the given request code (or ORing it with 0x100 since only first 8 bits can be used as request code).

    As a quick and dirty fix, I had to write the following to make it works:

    @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            Let.handle(this, requestCode & 0xff, permissions, grantResults);
        }
    
    opened by renaudcerrato 1
  • Delayed execution failed error when asking for multiple permissions

    Delayed execution failed error when asking for multiple permissions

    For your reference.

    FATAL EXCEPTION: main
    
    Process: com.mobility.iz.tstar, PID: 13347
    
    java.lang.RuntimeException: Failure delivering result ResultInfo{who=@android:requestPermissions:, request=0, result=-1, data=Intent { act=android.content.pm.action.REQUEST_PERMISSIONS (has extras) }} to activity {com.mobility.iz.tstar/com.mobility.ui.DashboardActivity}: com.canelmas.let.LetException: Delayed Execution Failed!
    
        at android.app.ActivityThread.deliverResults(ActivityThread.java:3699)
    
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742)
    
        at android.app.ActivityThread.-wrap16(ActivityThread.java)
    
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393)
    
        at android.os.Handler.dispatchMessage(Handler.java:102)
    
        at android.os.Looper.loop(Looper.java:148)
    
        at android.app.ActivityThread.main(ActivityThread.java:5417)
    
        at java.lang.reflect.Method.invoke(Native Method)
    
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
    
     Caused by: com.canelmas.let.LetException: Delayed Execution Failed!
    
        at com.canelmas.let.Let.handle(Let.java:74)
    
        at com.mobility.ui.DashboardActivity.onRequestPermissionsResult(DashboardActivity.java:71)
    
        at android.app.Activity.dispatchRequestPermissionsResult(Activity.java:6582)
    
        at android.app.Activity.dispatchActivityResult(Activity.java:6460)
    
        at android.app.ActivityThread.deliverResults(ActivityThread.java:3695)
    
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742) 
    
        at android.app.ActivityThread.-wrap16(ActivityThread.java) 
    
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393) 
    
        at android.os.Handler.dispatchMessage(Handler.java:102) 
    
        at android.os.Looper.loop(Looper.java:148) 
    
        at android.app.ActivityThread.main(ActivityThread.java:5417) 
    
        at java.lang.reflect.Method.invoke(Native Method) 
    
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
    
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
    
     Caused by: com.canelmas.let.LetException: Future Task execution failed!
    
        at com.canelmas.let.DelayedTasks$Task.call(DelayedTasks.java:68)
    
        at com.canelmas.let.DelayedTasks$Task.execute(DelayedTasks.java:59)
    
        at com.canelmas.let.Let.handle(Let.java:72)
    
        at com.mobility.ui.DashboardActivity.onRequestPermissionsResult(DashboardActivity.java:71) 
    
        at android.app.Activity.dispatchRequestPermissionsResult(Activity.java:6582) 
    
        at android.app.Activity.dispatchActivityResult(Activity.java:6460) 
    
        at android.app.ActivityThread.deliverResults(ActivityThread.java:3695) 
    
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742) 
    
        at android.app.ActivityThread.-wrap16(ActivityThread.java) 
    
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393) 
    
        at android.os.Handler.dispatchMessage(Handler.java:102) 
    
        at android.os.Looper.loop(Looper.java:148) 
    
        at android.app.ActivityThread.main(ActivityThread.java:5417) 
    
        at java.lang.reflect.Method.invoke(Native Method) 
    
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
    
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
    
     Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    
        at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1489)
    
        at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1507)
    
        at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:634)
    
        at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:613)
    
        at com.mobility.ui.DashboardActivity.init_aroundBody0(DashboardActivity.java:65)
    
        at com.mobility.ui.DashboardActivity$AjcClosure1.run(DashboardActivity.java:1)
    
        at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:149)
    
        at com.canelmas.let.DelayedTasks$Task.call(DelayedTasks.java:66)
    
        at com.canelmas.let.DelayedTasks$Task.execute(DelayedTasks.java:59) 
    
        at com.canelmas.let.Let.handle(Let.java:72) 
    
        at com.mobility.ui.DashboardActivity.onRequestPermissionsResult(DashboardActivity.java:71) 
    
        at android.app.Activity.dispatchRequestPermissionsResult(Activity.java:6582) 
    
        at android.app.Activity.dispatchActivityResult(Activity.java:6460) 
    
        at android.app.ActivityThread.deliverResults(ActivityThread.java:3695) 
    
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742) 
    
        at android.app.ActivityThread.-wrap16(ActivityThread.java) 
    
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393) 
    
        at android.os.Handler.dispatchMessage(Handler.java:102) 
    
        at android.os.Looper.loop(Looper.java:148) 
    
        at android.app.ActivityThread.main(ActivityThread.java:5417) 
    
        at java.lang.reflect.Method.invoke(Native Method) 
    
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
    
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
    

    P.S. Thanks a ton for the library! :+1:

    opened by izBasit 1
  • Lower 8 bits for requestCode

    Lower 8 bits for requestCode

    Only 8 bits request codes should be used for requestPermissions(Activity activity, String[] permissions, int requestCode)

    Otherwise java.lang.IllegalArgumentException: Can only use lower 8 bits for requestCode is thrown.

    Fix : #10

    bug 
    opened by canelmas 0
  • Add a Gitter chat badge to README.md

    Add a Gitter chat badge to README.md

    canelmas/let now has a Chat Room on Gitter

    @canelmas has just created a chat room. You can visit it here: https://gitter.im/canelmas/let.

    This pull-request adds this badge to your README.md:

    Gitter

    If my aim is a little off, please let me know.

    Happy chatting.

    PS: Click here if you would prefer not to receive automatic pull-requests from Gitter in future.

    opened by gitter-badger 0
  • Let beta wrong dependency included

    Let beta wrong dependency included

    I tried updating Let from 0.1.11 to the new beta version 1.0.0-beta1 (also tested 1.0.0-beta2). Gradle build the APK without any errors but for some reason it included the wrong Javax.activation.* files, this is giving me errors for the JavaMail library that I am using.

    I have no idea why this is happening, and after downgrading back to 0.1.11 it was fixed. I made a ticket in JavaMail first because I thought there was an issue with JavaMail: https://github.com/eclipse-ee4j/javamail/issues/378

    Even though after downgrading it was fixed for me I would like to know if this can be investigated and fixed in future versions.

    If you need any info please let me know.

    opened by Glennmen 0
  • let-plugin: build.gradle should use 'implementation' instead of 'compile'

    let-plugin: build.gradle should use 'implementation' instead of 'compile'

    This'll stop this warning from appearing when Let is used in a project:

    WARNING: Configuration 'compile' is obsolete and has been replaced with 'implementation' and 'api'.

    opened by fridtjof 0
  • Button set text not working when method has @AskPermission annotation

    Button set text not working when method has @AskPermission annotation

    Hi, it seems there is a problem while I'm trying to update the text of the button in a method like this :

    
       @AskPermission({
                Manifest.permission.SEND_SMS,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.INTERNET
        })
        private void ChangeServiceRunning() {
    
           btn.setText("Stop Service!");
           btn.setBackgroundResource(R.drawable.round_active_button);
    
        }
    
    
    opened by pouyaSamie 0
Releases(1.0.0-beta2)
Owner
Can Elmas
Technology
Can Elmas
A simple utility to remove unused resources in your Android app to lower the size of the APK. It's based on the Android lint tool output.

android-resource-remover android-resource-remover is utility that removes unused resources reported by Android Lint from your project. The goal is to

Keepsafe 1.3k Dec 16, 2022
Dynamic Delivery everywhere through a common API

A set of tools geared towards making Dynamic Delivery universally available, regardless of underlying App Store / distribution platform, while also providing a single unified Android client API and a streamlined developer experience.

Jesper Åman 272 Dec 9, 2022
An easy to use translation / localization api written in Kotlin that can be used with Java and Kotlin

KTranslate KTranslate is an easy to use TranslationAPI written in Kotlin, with currently 26 supported languages. It is very easy to use in Kotlin and

Felix Beinssen 0 Sep 2, 2022
Simple library to generate and view PDF in Android

PDFCreatorAndroid Simple library to generate and view PDF in Android A simple library to create and view PDF with zero dependency Or native code. Add

Tej Pratap Singh 234 Dec 11, 2022
Simple Android SharedPreferences wrapper.

Prefs Simple Android SharedPreferences wrapper. Repository Add this in your root build.gradle file (not your module build.gradle file): allprojects {

null 130 Nov 11, 2022
A tool to install components of the Android SDK into a Maven repository or repository manager to use with the Android Maven Plugin, Gradle and other tools.

Maven Android SDK Deployer Original author including numerous fixes and changes: Manfred Moser [email protected] at simpligility technologies i

simpligility 1.4k Dec 27, 2022
This is a Android Studio/ IntelliJ IDEA plugin to localize your Android app, translate your string resources automactically.

#Android Localizationer This is a Android Studio/ IntelliJ IDEA plugin to localize your Android app, translate your string resources automactically. T

Wesley Lin 822 Dec 8, 2022
A tool to install components of the Android SDK into a Maven repository or repository manager to use with the Android Maven Plugin, Gradle and other tools.

Maven Android SDK Deployer Original author including numerous fixes and changes: Manfred Moser [email protected] at simpligility technologies i

simpligility 1.4k Dec 27, 2022
Automated-build-android-app-with-github-action - CI/CD Automated Build Android App Bundle / APK / Signed With Github Action

Automated Build Android With Using Github Action Project Github Action Script Us

Faisal Amir 34 Dec 19, 2022
proguard resource for Android by wechat team

AndResGuard Read this in other languages: English, 简体中文. AndResGuard is a tooling for reducing your apk size, it works like the ProGuard for Java sour

shwenzhang 8.1k Jan 9, 2023
A super fast build tool for Android, an alternative to Instant Run

Freeline Freeline is a super fast build tool for Android and an alternative to Instant Run. Caching reusable class files and resource indices, it enab

Alibaba 5.5k Jan 2, 2023
Command-line tool to count per-package methods in Android .dex files

dex-method-counts Simple tool to output per-package method counts in an Android DEX executable grouped by package, to aid in getting under the 65,536

Mihai Parparita 2.6k Nov 25, 2022
View Inspection Toolbar for Android Development

View Inspector Plugin View inspection toolbar for android development. Features Boundary show outlines show margins show paddings Layer Scalpel featur

Fumihiro Xue (Peter Hsieh) 2.2k Nov 14, 2022
Make Android screenshots of scrollable screen content

scrollscreenshot Make Android screenshots of scrollable screen content - brought to you by PGS Software SA This tool makes a number of screenshots, sc

PGS Software 714 Dec 7, 2022
[] Dissect layout traversals on Android

Probe Dissect layout traversals on Android. Features Intercept View methods. onMeasure(int, int) onLayout(boolean, int, int, int, int) draw(Canvas) an

Lucas Rocha 555 Nov 25, 2022
Android Library Finder

alfi Android Library Finder Search through thousands of android libraries that can help you scale your projects elegantly Usage Search for something a

César Ferreira 509 Dec 8, 2022
Combines tools for fast android app devlopment

Android - Rapid Test Driven Development Combine tools to generate most of the boilerplate code. Examples how to test different aspects of an android a

Nico Küchler 379 Nov 25, 2022
Make mosaic effect on android

ProMosaic Make mosaic for image on android. Features Select Mode Follow finger Select rectangle Effect Mode Grid color based on original image Blur Im

dawson 359 Dec 29, 2022