Last active
October 21, 2018 07:38
-
-
Save jaisonfdo/1f49a5d52d61d90478da4173ddc5e3fb to your computer and use it in GitHub Desktop.
DemoAppsSampleCode
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Instantiate an AlertDialog.Builder with its constructor | |
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); | |
AlertDialog.Builder alertDialogBuilder1 = new AlertDialog.Builder(new ContextThemeWrapper(this, | |
android.R.style.Theme_DeviceDefault_Light_Dialog)); | |
// You can replace your own theme by replacing the themeID | |
AlertDialog.Builder alertDialogBuilder2 = new AlertDialog.Builder(this,themeID); | |
//set the title to be appear in the dialog | |
alertDialogBuilder.setTitle("Draft"); | |
//sets the message to be displayed in the alert dialog | |
alertDialogBuilder.setMessage("Discard draft?"); | |
//sets the property that the dialog can be cancelled or not | |
alertDialogBuilder.setCancelable(false); | |
// set the icon of the alert dialog box | |
alertDialogBuilder.setIcon(android.R.drawable.ic_dialog_alert); | |
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); | |
alertDialogBuilder.setTitle("Draft"); | |
alertDialogBuilder.setMessage("Discard draft?"); | |
alertDialogBuilder.setCancelable(false); | |
alertDialogBuilder.setIcon(android.R.drawable.ic_dialog_alert); | |
alertDialogBuilder.setPositiveButton("DISCARD", new DialogInterface.OnClickListener() { | |
public void onClick(DialogInterface dialog, int which) { | |
// continue with discard | |
Toast.makeText(AlertDialogActivity.this, "Discard", Toast.LENGTH_SHORT).show(); | |
} | |
}); | |
alertDialogBuilder.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() { | |
public void onClick(DialogInterface dialog, int which) { | |
// do nothing | |
Toast.makeText(AlertDialogActivity.this, "Cancel", Toast.LENGTH_SHORT).show(); | |
} | |
}); | |
alertDialogBuilder.show(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Displays the AlertDialog with 3 Action buttons | |
* you can set cancelable property | |
*/ | |
public void showAlertDialog(String title,String message,String positive, | |
String negative,String neutral,final int from,boolean isCancelable) | |
public interface AlertDialogListener | |
{ | |
public void onPositiveClick(int from); | |
public void onNegativeClick(int from); | |
public void onNeutralClick(int from); | |
} | |
AlertDialogHelper alertDialogHelper; | |
alertDialogHelper = new AlertDialogHelper(this); | |
// Request code – 1 | |
alertDialogHelper.showAlertDialog("Draft","Discard draft ?","Discard","Cancel",1,false); | |
public class AlertDialogSampleActivity extends AppCompatActivity implements AlertDialogHelper.AlertDialogListener | |
{ | |
// Callback Methods | |
@Override | |
public void onPositiveClick(int from) { | |
Toast.makeText(getApplicationContext(), "Positive Click from :"+from, Toast.LENGTH_LONG).show(); | |
} | |
@Override | |
public void onNegativeClick(int from) { | |
Toast.makeText(getApplicationContext(), "Negative Click from :"+from, Toast.LENGTH_LONG).show(); | |
} | |
@Override | |
public void onNeutralClick(int from) { | |
Toast.makeText(getApplicationContext(), "Neutral Click from :"+from, Toast.LENGTH_LONG).show(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Make HTTP Calls | |
implementation 'com.squareup.retrofit2:retrofit:2.3.0' | |
// Used to convert Java Objects into their JSON | |
implementation 'com.squareup.retrofit2:converter-gson:2.3.0' | |
public class UserDetails { | |
private Details details; | |
public Details getDetails() { | |
return details; | |
} | |
public void setDetails(Details details) { | |
this.details = details; | |
} | |
} | |
public interface ApiInterface | |
{ | |
@GET("user") | |
Call<UserDetails> getUser(@QueryMap Map<String, String> params); | |
@POST("user") | |
Call<UserDetails> postUser(@QueryMap Map<String, String> params); | |
@PUT("user") | |
Call<UserDetails> updateUser(@QueryMap Map<String, String> params); | |
} | |
public class ApiClient | |
{ | |
public static final String BASE_URL = "http://testapi.droidmentor.com/"; | |
private static Retrofit retrofit = null; | |
public static Retrofit getClient() { | |
if (retrofit==null) { | |
retrofit = new Retrofit.Builder() | |
.baseUrl(BASE_URL) | |
.addConverterFactory(GsonConverterFactory.create()) | |
.build(); | |
} | |
return retrofit; | |
} | |
} | |
Retrofit.Builder builder=new Retrofit.Builder() | |
.baseUrl(BASE_URL) | |
.client(get_HTTPClient(headers)) | |
.addConverterFactory(GsonConverterFactory.create()); | |
private static OkHttpClient get_HTTPClient(final Map<String, String> headers) | |
{ | |
final OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); | |
httpClient.addInterceptor(new Interceptor() { | |
@Override | |
public Response intercept(Interceptor.Chain chain) throws IOException { | |
Request original = chain.request(); | |
// Request customization: add request headers | |
Request.Builder requestBuilder = original.newBuilder(); // <– this is the important line | |
for (Map.Entry<String, String> pairs : headers.entrySet()) { | |
if (pairs.getValue() != null) { | |
requestBuilder.header(pairs.getKey(), pairs.getValue()); | |
} | |
} | |
requestBuilder.method(original.method(), original.body()); | |
Request request = requestBuilder.build(); | |
return chain.proceed(request); | |
} | |
}); | |
return httpClient.build(); | |
} | |
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class); | |
public void getUserDetails() | |
{ | |
Map<String, String> queryParams = new HashMap<>(); | |
queryParams.put("user[email]",etUserEmail.getText().toString()); | |
ProgressDialogLoader.progressdialog_creation(this, "Loading"); | |
Call<UserDetails> call = apiService.getUser(queryParams); | |
call.enqueue(new Callback<UserDetails>() { | |
@Override | |
public void onResponse(Call<UserDetails>call, Response<UserDetails> response) { | |
if(response.body().getDetails()!=null) | |
{ | |
Log.d(TAG, "User ID: " + response.body().getDetails().getId()); | |
etUserName.setText(response.body().getDetails().getName()); | |
} | |
else | |
{ | |
Toast.makeText(getApplicationContext(), "User does not exist", Toast.LENGTH_SHORT).show(); | |
Log.d(TAG, "User details does not exist"); | |
} | |
ProgressDialogLoader.progressdialog_dismiss(); | |
} | |
@Override | |
public void onFailure(Call<UserDetails>call, Throwable t) { | |
Log.e(TAG, t.toString()); | |
ProgressDialogLoader.progressdialog_dismiss(); | |
} | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
release_keystore_path=/Path/Of/Your/Keystore/KeyStoreName.jks | |
release_keystore_password=P@ssword1 | |
release_key_alias=KeyIdentifier | |
release_key_alias_password=KeyP@ssword1 | |
Properties keystoreProperties = new Properties(); | |
keystoreProperties.load(new FileInputStream(file(rootProject.file("gradle.properties")))) | |
signingConfigs | |
{ | |
release | |
{ | |
storeFile file(keystoreProperties["release_keystore_path"]) | |
storePassword keystoreProperties["release_keystore_password"] | |
keyAlias keystoreProperties["release_key_alias"] | |
keyPassword keystoreProperties["release_key_alias_password"] | |
} | |
} | |
buildTypes | |
{ | |
release | |
{ | |
shrinkResources true | |
minifyEnabled true | |
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’ | |
signingConfig signingConfigs.release | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
implementation 'org.altbeacon:android-beacon-library:2.15.2' | |
<uses-permission android:name="android.permission.BLUETOOTH" /> | |
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> | |
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> | |
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION" /> | |
public class BeaconTransmitterActivity extends AppCompatActivity implements | |
BluetoothListener.OnBluetoothSupportedCheckListener,BluetoothListener.OnBluetoothEnabledCheckListener, | |
BluetoothListener.BluetoothTrigger | |
{ | |
BluetoothHelper bluetoothHelper; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
bluetoothHelper = new BluetoothHelper(); | |
bluetoothHelper.initializeBluetooth(this); | |
} | |
} | |
public class MyApplication extends Application { | |
BackgroundPowerSaver backgroundPowerSaver; | |
BeaconManager beaconManager; | |
@Override | |
public void onCreate() { | |
super.onCreate(); | |
beaconManager = BeaconManager.getInstanceForApplication(this); | |
// enables auto battery saving of about 60% | |
backgroundPowerSaver = new BackgroundPowerSaver(this); | |
} | |
} | |
String major, minor, uuid; | |
uuid = etUUID.getText().toString().trim(); | |
major = etMajorValue.getText().toString().trim(); | |
minor = etMinorValue.getText().toString().trim(); | |
beacon = new Beacon.Builder().setId1(uuid).setId2(major).setId3(minor) | |
.setManufacturer(0x0118) | |
.setTxPower(-59) | |
.setDataFields(Arrays.asList(new Long[]{6l, 7l})) .build(); | |
// Change the layout below for other beacon types | |
beaconParser = new BeaconParser() | |
.setBeaconLayout(parserLayout[beaconLayout]); | |
beaconTransmitter = new BeaconTransmitter(getApplicationContext(), beaconParser); | |
beaconTransmitter.startAdvertising(beacon, new AdvertiseCallback() { | |
@Override | |
public void onStartSuccess(AdvertiseSettings settingsInEffect) { | |
super.onStartSuccess(settingsInEffect); | |
} | |
@Override | |
public void onStartFailure(int errorCode) { | |
super.onStartFailure(errorCode); | |
} | |
}); | |
beaconParser = new BeaconParser().setBeaconLayout("##FORMAT##"); | |
// Replace the ##FORMAT## for the corrosponding beacon types | |
ALTBEACON m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25 | |
IBEACON m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24 | |
EDDYSTONE TLM x,s:0-1=feaa,m:2-2=20,d:3-3,d:4-5,d:6-7,d:8-11,d:12-15 | |
EDDYSTONE UID s:0-1=feaa,m:2-2=00,p:3-3:-41,i:4-13,i:14-19 | |
EDDYSTONE URL s:0-1=feaa,m:2-2=10,p:3-3:-41,i:4-20v | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
implementation 'com.android.support:design:27.1.0' | |
<android.support.design.widget.BottomNavigationView | |
android:id="@+id/bottom_navigation" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:layout_alignParentBottom="true" | |
app:itemBackground="@color/colorPrimary" | |
app:itemIconTint="@drawable/bottom_navigation_color_selector" | |
app:itemTextColor="@drawable/bottom_navigation_color_selector" | |
app:menu="@menu/menu_bottom_navigation" /> | |
app:menu="@menu/menu_bottom_navigation" | |
<?xml version="1.0" encoding="utf-8"?> | |
<menu xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto"> | |
<item | |
android:id="@+id/action_call" | |
android:icon="@drawable/ic_call" | |
android:title="@string/tab1" | |
app:showAsAction="ifRoom" /> | |
<item | |
android:id="@+id/action_chat" | |
android:icon="@drawable/ic_chat" | |
android:title="@string/tab2" | |
app:showAsAction="ifRoom" /> | |
<item | |
android:id="@+id/action_contact" | |
android:enabled="false" | |
android:icon="@drawable/ic_contact" | |
android:title="@string/tab3" | |
app:showAsAction="ifRoom" /> | |
</menu> | |
<?xml version="1.0" encoding="utf-8"?> | |
<selector xmlns:android="http://schemas.android.com/apk/res/android"> | |
<item android:color="@color/tab_enable" android:state_enabled="true" /> | |
<item android:color="@color/tab_disable" android:state_enabled="false" /> | |
</selector> | |
//Initializing the bottomNavigationView | |
bottomNavigationView = (BottomNavigationView)findViewById(R.id.bottom_navigation); | |
bottomNavigationView.setOnNavigationItemSelectedListener( | |
new BottomNavigationView.OnNavigationItemSelectedListener() { | |
@Override | |
public boolean onNavigationItemSelected(@NonNull MenuItem item) { | |
switch (item.getItemId()) { | |
case R.id.action_call: | |
break; | |
case R.id.action_chat: | |
break; | |
case R.id.action_contact: | |
break; | |
} | |
return false; | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<selector xmlns:android="http://schemas.android.com/apk/res/android"> | |
<item android:color="@color/colorAccent" android:state_checked="true" /> | |
<item android:color="@color/colorTextPrimary" android:state_checked="false" /> | |
</selector> | |
<?xml version="1.0" encoding="utf-8"?> | |
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:id="@+id/activity_main" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
tools:context="droidmentor.bnv_with_viewpager.MainActivity"> | |
<android.support.design.widget.BottomNavigationView | |
android:id="@+id/bottom_navigation" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:layout_alignParentBottom="true" | |
app:itemBackground="@color/colorPrimary" | |
app:itemIconTint="@drawable/bottom_navigation_color_selector" | |
app:itemTextColor="@drawable/bottom_navigation_color_selector" | |
app:menu="@menu/menu_bottom_navigation" /> | |
<android.support.v4.view.ViewPager | |
android:id="@+id/viewpager" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:layout_above="@+id/bottom_navigation" | |
android:layout_alignParentTop="true" | |
app:layout_behavior="@string/appbar_scrolling_view_behavior" /> | |
</RelativeLayout> | |
private void setupViewPager(ViewPager viewPager) | |
{ | |
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); | |
callsFragment=new CallsFragment(); | |
chatFragment=new ChatFragment(); | |
contactsFragment=new ContactsFragment(); | |
adapter.addFragment(callsFragment); | |
adapter.addFragment(chatFragment); | |
adapter.addFragment(contactsFragment); | |
viewPager.setAdapter(adapter); | |
} | |
bottomNavigationView.setOnNavigationItemSelectedListener( | |
new BottomNavigationView.OnNavigationItemSelectedListener() { | |
@Override | |
public boolean onNavigationItemSelected(@NonNull MenuItem item) { | |
switch (item.getItemId()) { | |
case R.id.action_call: | |
viewPager.setCurrentItem(0); | |
break; | |
case R.id.action_chat: | |
viewPager.setCurrentItem(1); | |
break; | |
case R.id.action_contact: | |
viewPager.setCurrentItem(2); | |
break; | |
} | |
return false; | |
} | |
}); | |
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { | |
@Override | |
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { | |
} | |
@Override | |
public void onPageSelected(int position) { | |
if (prevMenuItem != null) | |
prevMenuItem.setChecked(false); | |
else | |
bottomNavigationView.getMenu().getItem(0).setChecked(false); | |
bottomNavigationView.getMenu().getItem(position).setChecked(true); | |
prevMenuItem = bottomNavigationView.getMenu().getItem(position); | |
} | |
@Override | |
public void onPageScrollStateChanged(int state) { | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
dependencies { | |
implementation 'com.android.support:design:27.1.0' | |
} | |
<?xml version="1.0" encoding="utf-8"> | |
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
android:id="@+id/coordinator" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
tools:context="droidmentor.checking.BottomSheet.BottomSheetExample"> | |
<LinearLayout | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:background="#F3F3F3" | |
android:orientation="vertical" | |
android:paddingBottom="@dimen/inbetween_space" | |
android:paddingLeft="@dimen/activity_vertical_margin" | |
android:paddingRight="@dimen/activity_vertical_margin" | |
android:paddingTop="@dimen/inbetween_space" | |
app:behavior_hideable="false" | |
app:behavior_peekHeight="@dimen/bs_peek_height" | |
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"> | |
</LinearLayout> | |
</android.support.design.widget.CoordinatorLayout> | |
View persistentbottomSheet = coordinatorLayout.findViewById(R.id.bottomsheet); | |
final BottomSheetBehavior behavior = BottomSheetBehavior.from(persistentbottomSheet); | |
if (behavior != null) | |
behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { | |
@Override | |
public void onStateChanged(@NonNull View bottomSheet, int newState) { | |
// React to state change | |
//showing the different states | |
switch (newState) { | |
case BottomSheetBehavior.STATE_HIDDEN: | |
break; | |
case BottomSheetBehavior.STATE_EXPANDED: | |
break; | |
case BottomSheetBehavior.STATE_COLLAPSED: | |
break; | |
case BottomSheetBehavior.STATE_DRAGGING: | |
break; | |
case BottomSheetBehavior.STATE_SETTLING: | |
break; | |
} | |
} | |
@Override | |
public void onSlide(@NonNull View bottomSheet, float slideOffset) { | |
// React to dragging events | |
} | |
}); | |
View modalbottomsheet = getLayoutInflater().inflate(R.layout.bottom_sheet_sample, null); | |
BottomSheetDialog dialog = new BottomSheetDialog(this); | |
dialog.setContentView(modalbottomsheet); | |
dialog.setCanceledOnTouchOutside(false); | |
dialog.setCancelable(false); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
implementation 'com.android.support:customtabs:27.1.0' | |
// Loading url | |
String url = "https://droidmentor.com/"; | |
// CustomTabsIntent.Builder used to configure CustomTabsIntent. | |
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); | |
// CustomTabsIntent used to launch the URL | |
CustomTabsIntent customTabsIntent = builder.build(); | |
// Open the Custom Tab | |
customTabsIntent.launchUrl(this, Uri.parse(url)); | |
// Change the background color of the Toolbar. | |
builder.setToolbarColor(ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null)); | |
// Configure custom enter and exit animations | |
builder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left); | |
builder.setExitAnimations(this, R.anim.slide_in_left, R.anim.slide_out_right); | |
// Setting a custom back button | |
builder.setCloseButtonIcon(BitmapFactory.decodeResource( | |
getResources(), R.drawable.ic_arrow_back)); | |
// Hide status bar once the user scrolls down the content | |
builder.enableUrlBarHiding(); | |
builder.setActionButton(BitmapFactory.decodeResource(getResources(), | |
R.drawable.ic_share), "Share", getItem(), true); | |
private PendingIntent getItem() | |
{ | |
Intent shareIntent = new Intent(Intent.ACTION_SEND); | |
shareIntent.setType("text/plain"); | |
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "Droidmentor is a site, which contains android tutorials"); | |
Log.d(TAG, "setMenuItem: "+url); | |
shareIntent.putExtra(Intent.EXTRA_TEXT, url); | |
return PendingIntent.getActivity(this, 0, shareIntent, 0); | |
} | |
// Title and Callback PendingIntent as parameters | |
builder.addMenuItem("Share", getItem()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<activity android:name=".DeepLinking.DeeplinkActivity"> | |
<intent-filter android:autoVerify="true"> | |
<action android:name="android.intent.action.VIEW" /> | |
<category android:name="android.intent.category.DEFAULT" /> | |
<category android:name="android.intent.category.BROWSABLE" /> | |
<!– URI https://www.droidmentor.com –> | |
<data | |
android:host="www.droidmentor.com" | |
android:scheme="http" /> | |
<!– URI https://droidmentor.com –> | |
<data | |
android:host="droidmentor.com" | |
android:scheme="http" /> | |
</intent-filter> | |
</activity> | |
protected void onCreate(Bundle savedInstanceState) | |
{ | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_deeplink); | |
Intent intent=getIntent(); | |
String action = intent.getAction(); | |
Uri data = intent.getData(); | |
if (data != null) { | |
String scheme = data.getScheme(); // "http" | |
String host = data.getHost(); // "droidmentor.com" | |
Log.d(TAG, "scheme: "+scheme); | |
Log.d(TAG, "host: "+host); | |
} | |
} | |
$ adb shell am start | |
-W -a android.intent.action.VIEW | |
-d < URI > < YOUR PACKAGE NAME > | |
$ adb shell am start | |
-W -a android.intent.action.VIEW | |
-d "https://droidmentor.com" droidmentor.deeplinking |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
buildscript { | |
repositories { | |
jcenter() | |
} | |
dependencies { | |
classpath ‘com.android.tools.build:gradle:2.2.0’ | |
} | |
} | |
apply plugin: ‘com.android.application’ | |
android { | |
compileSdkVersion 23 | |
buildToolsVersion "23.0.3" | |
defaultConfig { | |
applicationId "ur app ID" | |
minSdkVersion 16 | |
targetSdkVersion 23 | |
versionCode 1600010100 | |
versionName "1.1.0-alpha" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface ImageAttachmentListener | |
{ | |
public void image_attachment(int from, String filename, Bitmap file); | |
} | |
Imageutils imageutils; | |
imageutils =new Imageutils(this); | |
// Request code – 1 | |
imageutils.imagepicker(1); | |
public class ImageAttachmentActivity extends AppCompatActivity | |
implements ImageUtils.ImageAttachmentListener | |
{ | |
// Callback function | |
@Override | |
public void image_attachment(int from, String filename, Bitmap file) { | |
this.bitmap=file; | |
this.file_name=filename; | |
iv_attachment.setImageBitmap(file); | |
String path = Environment.getExternalStorageDirectory() + File.separator + "ImageAttach" + File.separator; | |
//Store the selected image into your desired location | |
imageutils.createImage(file,filename,path,false); | |
} | |
} | |
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<WebView | |
android:id="@+id/webView" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" /> | |
<uses-permission android:name="android.permission.INTERNET" /> | |
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | |
public class JavaScriptReceiver | |
{ | |
Context mContext; | |
/** Instantiate the receiver and set the context */ | |
JavaScriptReceiver(Context c) { | |
mContext = c; | |
} | |
@JavascriptInterface | |
public void showShopping(){ | |
Intent intent = new Intent(mContext, ShoppingActivity.class); | |
mContext.startActivity(intent); | |
} | |
@JavascriptInterface | |
public void showOrders(int orderid){ | |
Intent intent = new Intent(mContext, MyOrdersActivity.class); | |
intent.putExtra("order",orderid); | |
mContext.startActivity(intent); | |
} | |
} | |
WebView webView; | |
webView = (WebView) findViewById(R.id.webView); | |
embed_link = "http://droidmentor-app-callback.surge.sh/"; | |
if (TextUtils.isEmpty(embed_link)) | |
onBackPressed(); | |
webView.setWebChromeClient(new WebChromeClient()); | |
webView.getSettings().setDomStorageEnabled(true); | |
webView.getSettings().setJavaScriptEnabled(true); | |
<script> | |
function getRandomNumber(min,max) { | |
return Math.floor( Math.random() * ( 1 + min – max ) ) + max; | |
} | |
function showOrders() { | |
var number = getRandomNumber(10000,50000) | |
JSReceiver.showOrders(number); | |
} | |
function showShopping() { | |
JSReceiver.showShopping(); | |
} | |
</script> | |
<a href="#" class="btn-view-orders" onclick="showOrders()">View Orders</a> | |
<a href="#" onclick="showShopping()">Continue Shopping</a> | |
JavaScriptReceiver javaScriptReceiver; | |
javaScriptReceiver = new JavaScriptReceiver(this); | |
webView.addJavascriptInterface(javaScriptReceiver, "JSReceiver"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | |
<!– launcher Screen Background (Color/Image) –> | |
<item android:drawable="@drawable/ic_background1" /> | |
<!– App Logo which comes center of the Screen due to gravity:"center" –> | |
<item> | |
<bitmap | |
android:gravity="center" | |
android:src="@drawable/ic_logo" /> | |
</item> | |
</layer-list> | |
<style name="aluncher_theme" parent="Theme.AppCompat.NoActionBar"> | |
<item name="android:windowBackground">@drawable/launch_screen</item> | |
</style> | |
public class LauncherActivity extends Activity { | |
// launcher screen timer | |
private static int SPLASH_TIME_OUT = 1000; | |
Handler handler; | |
Runnable runnable; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
handler = new Handler(); | |
runnable = new Runnable() | |
{ | |
@Override | |
public void run() | |
{ | |
Intent home_activity=new Intent(LauncherActivity.this,HomepageActivity.class); | |
startActivity(home_activity); | |
finish(); | |
} | |
}; | |
handler.postDelayed(runnable, SPLASH_TIME_OUT); | |
} | |
@Override | |
public void onBackPressed() { | |
super.onBackPressed(); | |
handler.removeCallbacks(runnable); | |
} | |
} | |
<activity android:name=".LauncherActivity" | |
android:theme="@style/launcher_theme" | |
android:screenOrientation="portrait"> | |
<intent-filter> | |
<action android:name="android.intent.action.MAIN" /> | |
<category android:name="android.intent.category.LAUNCHER" /> | |
</intent-filter> | |
</activity> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MixPanelHelper mixPanelHelper; | |
mixPanelHelper=new MixPanelHelper(this); | |
mixPanelHelper.set_profile(); | |
//Track event with name | |
mixPanelHelper.triggerEvent("Login"); | |
//Add custom attributes with the events | |
JSONObject props=new JSONObject(); | |
props.put("$language","English"); | |
mixPanelHelper.triggerEvent("Event2",props); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
compile 'com.google.android.gms:play-services-location:16.0.0' | |
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> | |
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> | |
ArrayList<String> permissions=new ArrayList<>(); | |
PermissionUtils permissionUtils; | |
permissionUtils=new PermissionUtils(MyLocationUsingLocationAPI.this); | |
permissions.add(Manifest.permission.ACCESS_FINE_LOCATION); | |
permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION); | |
permissionUtils.check_permission(permissions,"Need GPS permission for getting your location",1); | |
GoogleApiClient mGoogleApiClient; | |
mGoogleApiClient = new GoogleApiClient.Builder(this) | |
.addConnectionCallbacks(this) | |
.addOnConnectionFailedListener(this) | |
.addApi(LocationServices.API).build(); | |
mGoogleApiClient.connect(); | |
public class MyLocationUsingLocationAPI extends AppCompatActivity implements ConnectionCallbacks,OnConnectionFailedListener,OnRequestPermissionsResultCallback, | |
PermissionResultCallback { | |
@Override | |
public void onConnectionFailed(ConnectionResult result) { | |
Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = "+ result.getErrorCode()); | |
} | |
@Override | |
public void onConnected(Bundle arg0) { | |
// Once connected with google api, get the location | |
} | |
@Override | |
public void onConnectionSuspended(int arg0) { | |
mGoogleApiClient.connect(); | |
} | |
} | |
LocationRequest mLocationRequest = new LocationRequest(); | |
mLocationRequest.setInterval(10000); | |
mLocationRequest.setFastestInterval(5000); | |
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); | |
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder() | |
.addLocationRequest(mLocationRequest); | |
PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi | |
.checkLocationSettings(mGoogleApiClient, builder.build()); | |
private boolean checkPlayServices() { | |
GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance(); | |
int resultCode = googleApiAvailability.isGooglePlayServicesAvailable(this); | |
if (resultCode != ConnectionResult.SUCCESS) { | |
if (googleApiAvailability.isUserResolvableError(resultCode)) { | |
googleApiAvailability.getErrorDialog(this,resultCode, | |
PLAY_SERVICES_REQUEST).show(); | |
} else { | |
Toast.makeText(getApplicationContext(), | |
"This device is not supported.", Toast.LENGTH_LONG).show(); | |
} | |
return false; | |
} | |
return true; | |
} | |
mLastLocation = LocationServices.FusedLocationApi | |
.getLastLocation(mGoogleApiClient); | |
public Address getAddress(double latitude,double longitude) | |
{ | |
Geocoder geocoder; | |
List<Address> addresses; | |
geocoder = new Geocoder(this, Locale.getDefault()); | |
try { | |
addresses = geocoder.getFromLocation(latitude,longitude, 1); // Here 1 represent max location result to returned, by documents it recommended 1 to 5 | |
return addresses.get(0); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public void showStandardNotification() { | |
int notificationId = new Random().nextInt(); | |
Intent intent = new Intent(this, NotificationActivity.class); | |
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0); | |
NotificationCompat.Builder notification =NotificationHelper.createNotificationBuider(this, | |
"Notification", "Standard notification!", R.drawable.ic_notifications, pIntent); | |
NotificationHelper.showNotification(this, notificationId, notification.build()); | |
} | |
public void showHeadsUpNotification() { | |
int notificationId = new Random().nextInt(); | |
Intent intent = new Intent(this, NotificationActivity.class); | |
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0); | |
Intent positive = new Intent(this, NotificationReceiver.class); | |
positive.putExtra("notiID", notificationId); | |
positive.setAction(POSITIVE_CLICK); | |
PendingIntent pIntent_positive = PendingIntent.getBroadcast(this, notificationId, positive, PendingIntent.FLAG_CANCEL_CURRENT); | |
Intent negative = new Intent(this, NotificationReceiver.class); | |
negative.putExtra("notiID", notificationId); | |
negative.setAction(NEGATIVE_CLICK); | |
PendingIntent pIntent_negative = PendingIntent.getBroadcast(this, notificationId, negative, PendingIntent.FLAG_CANCEL_CURRENT); | |
NotificationCompat.Builder notification = NotificationHelper.createNotificationBuider(this, | |
"Notification", "Heads-up notification", R.drawable.ic_notifications, pIntent); | |
notification.setPriority(Notification.PRIORITY_HIGH).setVibrate(new long[0]); | |
notification.addAction(new NotificationCompat.Action(R.drawable.ic_notifications, "Postive", pIntent_positive)); | |
notification.addAction(new NotificationCompat.Action(R.drawable.ic_notifications, "Negative", pIntent_negative)); | |
NotificationHelper.showNotification(this, notificationId, notification.build()); | |
} | |
public void showRemoteInputNotification() { | |
int notificationId = new Random().nextInt(); | |
NotificationCompat.Builder notification = NotificationHelper.createNotificationBuider(this, | |
"Remote Input", "Notification with Remote Input", R.drawable.ic_notifications); | |
notification.setGroupSummary(true); | |
notification.setGroup("KEY_NOTIFICATION_GROUP1"); | |
Intent remote_intent = new Intent(this, NotificationReceiver.class); | |
remote_intent.putExtra("notiID", notificationId); | |
remote_intent.setAction(REPLY_CLICK); | |
PendingIntent pIntent_positive = PendingIntent.getBroadcast(this, notificationId, remote_intent, PendingIntent.FLAG_CANCEL_CURRENT); | |
RemoteInput remoteInput = new RemoteInput.Builder(REPLY_TEXT_KEY) | |
.setLabel("Type Something") | |
.build(); | |
// Create the reply action and add the remote input. | |
NotificationCompat.Action action = | |
new NotificationCompat.Action.Builder(R.drawable.ic_send, "Reply", pIntent_positive) | |
.addRemoteInput(remoteInput) | |
.build(); | |
notification.addAction(action); | |
// Color change | |
notification.setColor(ContextCompat.getColor(this, R.color.colorAccent)); | |
NotificationHelper.showNotification(this, notificationId, notification.build()); | |
} | |
private CharSequence getMessageText(Intent intent) { | |
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); | |
if (remoteInput != null) { | |
return remoteInput.getCharSequence(NotificationHelper.REPLY_TEXT_KEY); | |
} | |
return null; | |
} | |
public void showSummaryNotification() { | |
int notificationId = new Random().nextInt(); | |
Intent intent = new Intent(this, NotificationActivity.class); | |
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0); | |
Bitmap largeIcon = BitmapFactory.decodeResource(getResources(), | |
R.drawable.icon_email); | |
NotificationCompat.Builder summary_notification = NotificationHelper.createNotificationBuider(this, | |
"2 new messages", "Bundle notification!", R.drawable.ic_call, pIntent); | |
summary_notification.setStyle(new NotificationCompat.InboxStyle() | |
.addLine("Ashwin Check this out") | |
.addLine("Ranjith Launch Party") | |
.setBigContentTitle("2 new messages") | |
.setSummaryText("2 new messages")) | |
.setLargeIcon(largeIcon) | |
.setGroupSummary(true) | |
.setContentIntent(pIntent) | |
.setGroup(GROUP_KEY).build(); | |
int notificationId1 = new Random().nextInt(); | |
NotificationCompat.Builder notification1 = NotificationHelper.createNotificationBuider(this, | |
"Ashwin", "Check this out", R.drawable.ic_notifications, pIntent); | |
int notificationId2 = new Random().nextInt(); | |
NotificationCompat.Builder notification2 = NotificationHelper.createNotificationBuider(this, | |
"Ranjith", "Launch Party", R.drawable.ic_notifications, pIntent); | |
NotificationHelper.showNotification(this, notificationId1, notification1.build()); | |
NotificationHelper.showNotification(this, notificationId2, notification2.build()); | |
NotificationHelper.showNotification(this, notificationId, summary_notification.build()); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<RelativeLayout | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<android.support.v4.view.ViewPager | |
android:id="@+id/pager_introduction" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:layout_above="@+id/viewPagerCountDots" /> | |
<LinearLayout | |
android:id="@+id/viewPagerCountDots" | |
android:layout_width="match_parent" | |
android:layout_height="@dimen/pager_height" | |
android:layout_alignParentLeft="true" | |
android:gravity="center" | |
android:orientation="horizontal" /> | |
</RelativeLayout> | |
<LinearLayout | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:orientation="vertical"> | |
<ImageView | |
android:layout_marginTop="50dp" | |
android:id="@+id/iv_onboard" | |
android:layout_width="match_parent" | |
android:layout_height="@dimen/ob_banner_height" | |
android:gravity="center" | |
android:src="@drawable/onboard_page1" /> | |
<RelativeLayout | |
android:layout_below="@+id/iv_onboard" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<LinearLayout | |
android:layout_centerVertical="true" | |
android:orientation="vertical" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content"> | |
<TextView | |
android:id="@+id/tv_header" | |
android:text="@string/ob_header1" | |
android:textSize="@dimen/ob_header_text_size" /> | |
<TextView | |
android:id="@+id/tv_desc" | |
android:text="@string/ob_desc1" | |
android:textColor="@color/ob_desc" | |
android:textSize="@dimen/ob_desc_text_size" /> | |
</LinearLayout> | |
</RelativeLayout> | |
</LinearLayout> | |
<?xml version="1.0" encoding="utf-8"?> | |
<shape xmlns:android="http://schemas.android.com/apk/res/android" | |
android:shape="oval" android:useLevel="true" | |
android:dither="true"> | |
<size android:height="@dimen/pager_dot_size" android:width="@dimen/pager_dot_size"/> | |
// Change the color based on the indicator states | |
<solid android:color="@color/selected_dot"/> | |
</shape> | |
public void loadData() | |
{ | |
int[] header = {R.string.ob_header1, R.string.ob_header2, R.string.ob_header3}; | |
int[] desc = {R.string.ob_desc1, R.string.ob_desc2, R.string.ob_desc3}; | |
int[] imageId = {R.drawable.onboard_page1, R.drawable.onboard_page2, R.drawable.onboard_page3}; | |
for(int i=0;i<imageId.length;i++) | |
{ | |
OnBoardItem item=new OnBoardItem(); | |
item.setImageID(imageId[i]); | |
item.setTitle(getResources().getString(header[i])); | |
item.setDescription(getResources().getString(desc[i])); | |
onBoardItems.add(item); | |
} | |
} | |
private ViewPager onboard_pager; | |
private OnBoard_Adapter mAdapter; | |
onboard_pager = (ViewPager) findViewById(R.id.pager_introduction); | |
mAdapter = new OnBoard_Adapter(this,onBoardItems); | |
onboard_pager.setAdapter(mAdapter); | |
// Slideup animation | |
<?xml version="1.0" encoding="utf-8"?> | |
<translate xmlns:android="http://schemas.android.com/apk/res/android" | |
android:fromYDelta="100%p" android:toYDelta="0%p" | |
android:duration="@android:integer/config_longAnimTime"/> | |
// Slidedown animation</pre> | |
<?xml version="1.0" encoding="utf-8"?> | |
<translate xmlns:android="http://schemas.android.com/apk/res/android" | |
android:fromYDelta="0%p" android:toYDelta="100%p" | |
android:duration="@android:integer/config_longAnimTime"/> | |
onboard_pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { | |
@Override | |
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { | |
} | |
@Override | |
public void onPageSelected(int position) { | |
// Change the current position intimation | |
for (int i = 0; i < dotsCount; i++) { | |
dots[i].setImageDrawable(ContextCompat.getDrawable(OnBoardingActivity.this, R.drawable.non_selected_item_dot)); | |
} | |
dots[position].setImageDrawable(ContextCompat.getDrawable(OnBoardingActivity.this, R.drawable.selected_item_dot)); | |
int pos=position+1; | |
if(pos==dotsCount&&previous_pos==(dotsCount-1)) | |
show_animation(); | |
else if(pos==(dotsCount-1)&&previous_pos==dotsCount) | |
hide_animation(); | |
previous_pos=pos; | |
} | |
@Override | |
public void onPageScrollStateChanged(int state) { | |
} | |
}); | |
private Button btn_get_started; | |
btn_get_started = (Button) findViewById(R.id.btn_get_started); | |
btn_get_started.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
Toast.makeText(OnBoardingActivity.this,"Redirect to wherever you want",Toast.LENGTH_LONG).show(); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if(Build.VERSION.SDK_INT >= 23) | |
if (checkAndRequestPermissions(permissions, request_code)) | |
permissionResultCallback.PermissionGranted(request_code); | |
else | |
permissionResultCallback.PermissionGranted(request_code); | |
int hasPermission = ContextCompat.checkSelfPermission(current_activity,permissions.get(i)); | |
if (hasPermission != PackageManager.PERMISSION_GRANTED) | |
listPermissionsNeeded.add(permissions.get(i)); | |
// Request Permissions | |
if (!listPermissionsNeeded.isEmpty()) | |
{ | |
ActivityCompat.requestPermissions(current_activity,listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),request_code); | |
return false; | |
} | |
// Check to show the rationale dialog | |
if(ActivityCompat.shouldShowRequestPermissionRationale(current_activity,listPermissionsNeeded.get(i))) | |
pending_permissions.add(listPermissionsNeeded.get(i)); | |
else{ | |
Log.i("Go to settings","and enable permissions"); | |
permissionResultCallback.NeverAskAgain(req_code); | |
Toast.makeText(current_activity, "Go to settings and enable permissions", Toast.LENGTH_LONG).show(); | |
return; | |
} | |
new AlertDialog.Builder(current_activity) | |
.setMessage(" Explain here why the app needs permissions ") | |
.setPositiveButton("Ok", okListener) | |
.setNegativeButton("Cancel", okListener) | |
.create() | |
.show(); | |
interface PermissionResultCallback | |
{ | |
void PermissionGranted(int request_code); | |
void PartialPermissionGranted(int request_code, ArrayList<String> granted_permissions); | |
void PermissionDenied(int request_code); | |
void NeverAskAgain(int request_code); | |
} | |
ArrayList<String>permissions=new ArrayList<>(); | |
permissions.add(Manifest.permission.ACCESS_FINE_LOCATION); | |
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); | |
permissionUtils=new PermissionUtils(getApplicationContext()); | |
permissionUtils.check_permission(permissions,"Explain here why the app needs permissions",1); | |
@Override | |
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, | |
@NonNull int[] grantResults) { | |
// redirects to utils | |
permissionUtils.onRequestPermissionsResult(requestCode,permissions,grantResults); | |
} | |
public class PermissionActivity extends AppCompatActivity implements | |
OnRequestPermissionsResultCallback,PermissionResultCallback | |
{ | |
// Callback functions | |
@Override | |
public void PermissionGranted(int request_code) { | |
Log.i("PERMISSION","GRANTED"); | |
} | |
@Override | |
public void PartialPermissionGranted(int request_code, ArrayList<String> granted_permissions) { | |
Log.i("PERMISSION PARTIALLY","GRANTED"); | |
} | |
@Override | |
public void PermissionDenied(int request_code) { | |
Log.i("PERMISSION","DENIED"); | |
} | |
@Override | |
public void NeverAskAgain(int request_code) { | |
Log.i("PERMISSION","NEVER ASK AGAIN"); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
buildTypes | |
{ | |
release | |
{ | |
shrinkResources true | |
minifyEnabled true | |
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’ | |
} | |
} | |
// Basic proguard rules | |
-optimizations !code/simplification/arithmetic | |
-keepattributes <em>Annotation</em> | |
-keepattributes InnerClasses | |
-keepattributes EnclosingMethod | |
-keep class *<em>.R$</em> | |
-dontskipnonpubliclibraryclasses | |
-forceprocessing | |
-optimizationpasses 5 | |
-overloadaggressively | |
// Removing logging code | |
-assumenosideeffects class android.util.Log { | |
public static *** d(…); | |
public static *** v(…); | |
public static *** i(…); | |
public static *** w(…); | |
public static *** e(…); | |
} | |
// The -dontwarn option tells ProGuard not to complain about some artefacts in the Scala runtime | |
-dontwarn android.support.** | |
-dontwarn android.app.Notification | |
-dontwarn org.apache.log4j.** | |
-dontwarn com.google.common.** | |
-keep class com.crashlytics.** { *; } | |
-keep class com.crashlytics.android.** | |
-keepattributes SourceFile,LineNumberTable | |
./gradlew assembleRelease |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
implementation 'com.android.billingclient:billing:1.1' | |
<!-- To use internet --> | |
<uses-permission android:name="android.permission.INTERNET" /> | |
PurchaseHelper purchaseHelper; | |
purchaseHelper = new PurchaseHelper(this, getPurchaseHelperListener()); | |
public PurchaseHelper.PurchaseHelperListener getPurchaseHelperListener() { | |
return new PurchaseHelper.PurchaseHelperListener() { | |
@Override | |
public void onServiceConnected(int resultCode) { | |
Log.d(TAG, "onServiceConnected: " + resultCode); | |
} | |
@Override | |
public void onSkuQueryResponse(List<SkuDetails> skuDetails) { | |
} | |
@Override | |
public void onPurchasehistoryResponse(List<Purchase> purchasedItems) { | |
purchaseHistory = purchasedItems; | |
} | |
@Override | |
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases) { | |
Log.d(TAG, "onPurchasesUpdated: " + responseCode); | |
} | |
}; | |
} | |
// You can pass BillingClient.SkuType.INAPP || BillingClient.SkuType.SUBS | |
purchaseHelper.getPurchasedItems(TYPE); | |
purchaseHelper.getSkuDetails(skuList,BillingClient.SkuType.INAPP); | |
purchaseHelper.launchBillingFLow(BillingClient.SkuType.INAPP, getProduct_id()); | |
purchaseHelper.gotoManageSubscription(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
implementation 'com.droidmentor:helper:1.0.1' | |
<!- Allows applications to open network sockets. -> | |
<uses-permission android:name="android.permission.INTERNET" /> | |
<!- Allows applications to access information about networks. -> | |
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | |
ApiClient.setBaseUrl("http://testapi.droidmentor.com/"); | |
Map<String, String> default_headers = new HashMap<>(); | |
default_headers.put("token", Url_handler.user_token); | |
ApiClient.setCommonHeaders(default_headers); | |
ApiClient.setNetworkErrorMessage("Network Error"); | |
ApiClient.showNetworkErrorMessage(false); | |
APICall networkCall; | |
networkCall = new APICall(this); | |
APICall networkCall; | |
Map<String, String> custom_headers = new HashMap<>(); | |
default_headers.put("token", Url_handler.user_token); | |
networkCall = new APICall(this,true,custom_headers); | |
// Standard Object | |
public APICall(Activity activity) { | |
init(activity, ApiClient.getCommonHeaders(), null); | |
} | |
// boolean flag decides the header is needed or not | |
public APICall(Activity activity, boolean with_header) { | |
if (with_header) | |
init(activity, ApiClient.getCommonHeaders(), null); | |
else | |
init(activity, null, null); | |
} | |
// you can add additional header | |
public APICall(Activity activity, boolean with_header, Map<String, String> customHeaders) { | |
if (with_header) | |
init(activity, ApiClient.getCommonHeaders(), customHeaders); | |
else | |
init(activity,null, customHeaders); | |
} | |
// set default header | |
public APICall(Activity activity, Map<String, String> defaultHeaders) { | |
init(activity, defaultHeaders, null); | |
} | |
// set default and additional header | |
public APICall(Activity activity, Map<String, String> defaultHeaders, Map<String, String> customHeaders) { | |
init(activity, defaultHeaders, customHeaders); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
apply plugin: ‘com.android.application’ | |
ext.majorVersion = 1 | |
ext.minorVersion = 2 | |
ext.patchVersion = 3 | |
ext.preRelease= "DEMO" | |
ext.minSdkVersion = 14 | |
android { | |
compileSdkVersion 23 | |
buildToolsVersion "23.0.2" | |
defaultConfig { | |
applicationId "droidmentor.sample" | |
targetSdkVersion 23 | |
minSdkVersion project.ext.minSdkVersion | |
versionCode generateVersionCode() // 140010203 | |
versionName generateVersionName() // 1.2.3-DEMO | |
} | |
} | |
private Integer generateVersionCode() { | |
return ext.minSdkVersion * 10000000 + ext.majorVersion * 10000 + ext.minorVersion * 100 + ext.patchVersion | |
} | |
private String generateVersionName() { | |
String versionName = "${ext.majorVersion}.${ext.minorVersion}.${ext.patchVersion}" | |
if (ext.preRelease != null && !ext.preRelease.isEmpty()) { | |
versionName = versionName + "-" + ext.preRelease | |
} | |
return versionName; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
implementation 'com.android.support:design:27.1.0' | |
Snackbar.make(view, "Message", SNACKBAR_DURATION).show(); | |
public void deleteContact() | |
{ | |
Snackbar snackbar = Snackbar | |
.make(coordinatorLayout, "Contact removed",SNACKBAR_DURATION) | |
.setAction("UNDO", new View.OnClickListener() { | |
@Override | |
public void onClick(View view) { | |
Log.d("", " The contact is restored "); | |
} | |
}); | |
View snackView = snackbar.getView(); | |
snackView.setBackgroundColor(ContextCompat.getColor(this, R.color.snackBarBg)); | |
TextView snackViewText = (TextView) snackView.findViewById(android.support.design.R.id.snackbar_text); | |
snackViewText.setTextColor(ContextCompat.getColor(this, R.color.colorWhite)); | |
Button snackViewButton = (Button) snackView.findViewById(android.support.design.R.id.snackbar_action); | |
snackViewButton.setTextColor(ContextCompat.getColor(this, R.color.colorAccent)); | |
// You can replace the above lines by using the below single line | |
//snackbar=SnackBarUtil.getColorfulSnackBar(snackbar,this,R.color.snackBarBg,R.color.colorWhite,R.color.colorAccent); | |
snackbar.show(); | |
} | |
public void checkStorage() | |
{ | |
final Snackbar customSnackBar = Snackbar.make(coordinatorLayout, "", SNACKBAR_DURATION); | |
Snackbar.SnackbarLayout layout = (Snackbar.SnackbarLayout) customSnackBar.getView(); | |
View customsnackView = getLayoutInflater().inflate(R.layout.storage_check, null); | |
TextView tvConnection = (TextView) customsnackView.findViewById(R.id.tv_connection); | |
ImageView ivConnection = (ImageView) customsnackView.findViewById(R.id.iv_connection); | |
// We can also customize the above controls | |
layout.setPadding(0,0,0,0); | |
layout.addView(customsnackView, 0); | |
customSnackBar.show(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<android.support.design.widget.TabLayout | |
android:id="@+id/tablayout" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:background="@color/colorPrimary" | |
app:tabGravity="fill" | |
app:tabMode="fixed" | |
app:tabIndicatorColor="@color/colorAccent" | |
app:tabSelectedTextColor="@color/colorTextPrimary" | |
app:tabTextColor="@color/colorTextDisable"/> | |
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { | |
@Override | |
public void onTabSelected(TabLayout.Tab tab) { | |
// while selecting the tab | |
} | |
@Override | |
public void onTabUnselected(TabLayout.Tab tab) { | |
// while unselect the Tab | |
} | |
@Override | |
public void onTabReselected(TabLayout.Tab tab) { | |
// while reselect the Tab | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
LocationHelper locationHelper; | |
locationHelper=new LocationHelper(this); | |
locationHelper.checkpermission(); | |
public class MyLocationUsingHelper extends AppCompatActivity implements ConnectionCallbacks, | |
OnConnectionFailedListener,OnRequestPermissionsResultCallback | |
@Override | |
protected void onActivityResult(int requestCode, int resultCode, Intent data) { | |
locationHelper.onActivityResult(requestCode,resultCode,data); | |
} | |
// Permission check functions | |
@Override | |
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, | |
@NonNull int[] grantResults) { | |
// redirects to utils | |
locationHelper.onRequestPermissionsResult(requestCode,permissions,grantResults) | |
} | |
/** | |
* Google connection callback methods | |
*/ | |
@Override | |
public void onConnectionFailed(ConnectionResult result) { | |
Log.i("Connection failed:", "" + result.getErrorCode()); | |
} | |
@Override | |
public void onConnected(Bundle arg0) { | |
// Once connected with google api, get the location | |
mLastLocation=locationHelper.getLocation(); | |
} | |
@Override | |
public void onConnectionSuspended(int arg0) { | |
locationHelper.connectApiClient(); | |
} | |
// check availability of play services | |
if (locationHelper.checkPlayServices()) | |
{ | |
// Building the GoogleApi client | |
locationHelper.buildGoogleApiClient(); | |
} | |
Location mLastLocation; | |
mLastLocation=locationHelper.getLocation(); | |
if (mLastLocation != null) { | |
latitude = mLastLocation.getLatitude(); | |
longitude = mLastLocation.getLongitude(); | |
Address locationAddress; | |
locationAddress=locationHelper.getAddress(latitude,longitude); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
LocationHelper locationHelper; | |
locationHelper=new LocationHelper(this); | |
locationHelper.checkpermission(); | |
public class MyLocationUsingHelper extends AppCompatActivity implements ConnectionCallbacks, | |
OnConnectionFailedListener,OnRequestPermissionsResultCallback | |
@Override | |
protected void onActivityResult(int requestCode, int resultCode, Intent data) { | |
locationHelper.onActivityResult(requestCode,resultCode,data); | |
} | |
// Permission check functions | |
@Override | |
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, | |
@NonNull int[] grantResults) { | |
// redirects to utils | |
locationHelper.onRequestPermissionsResult(requestCode,permissions,grantResults) | |
} | |
/** | |
* Google connection callback methods | |
*/ | |
@Override | |
public void onConnectionFailed(ConnectionResult result) { | |
Log.i("Connection failed:", "" + result.getErrorCode()); | |
} | |
@Override | |
public void onConnected(Bundle arg0) { | |
// Once connected with google api, get the location | |
mLastLocation=locationHelper.getLocation(); | |
} | |
@Override | |
public void onConnectionSuspended(int arg0) { | |
locationHelper.connectApiClient(); | |
} | |
// check availability of play services | |
if (locationHelper.checkPlayServices()) | |
{ | |
// Building the GoogleApi client | |
locationHelper.buildGoogleApiClient(); | |
} | |
Location mLastLocation; | |
mLastLocation=locationHelper.getLocation(); | |
if (mLastLocation != null) { | |
latitude = mLastLocation.getLatitude(); | |
longitude = mLastLocation.getLongitude(); | |
Address locationAddress; | |
locationAddress=locationHelper.getAddress(latitude,longitude); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<menu xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto"> | |
<item android:id="@+id/action_delete" | |
android:icon="@drawable/ic_delete" | |
android:title="@string/menu_item_delete" | |
app:showAsAction="always"/> | |
</menu> | |
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { | |
@Override | |
public boolean onCreateActionMode(ActionMode mode, Menu menu) { | |
// Inflate a menu resource providing context menu items | |
MenuInflater inflater = mode.getMenuInflater(); | |
inflater.inflate(R.menu.menu_multi_select, menu); | |
context_menu = menu; | |
return true; | |
} | |
@Override | |
public boolean onPrepareActionMode(ActionMode mode, Menu menu) { | |
return false; // Return false if nothing is done | |
} | |
@Override | |
public boolean onActionItemClicked(ActionMode mode, MenuItem item) { | |
return false; | |
} | |
@Override | |
public void onDestroyActionMode(ActionMode mode) { | |
} | |
}; | |
// Listening the click events | |
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this, recyclerView, | |
new RecyclerItemClickListener.OnItemClickListener() { | |
@Override | |
public void onItemClick(View view, int position) { | |
if (isMultiSelect) | |
multi_select(position); | |
else | |
Toast.makeText(getApplicationContext(), "Details Page", Toast.LENGTH_SHORT).show(); | |
} | |
@Override | |
public void onItemLongClick(View view, int position) { | |
if (!isMultiSelect) { | |
multiselect_list = new ArrayList<SampleModel>(); | |
isMultiSelect = true; | |
if (mActionMode == null) { | |
mActionMode = startActionMode(mActionModeCallback); | |
} | |
} | |
multi_select(position); | |
} | |
})); | |
// Add/Remove the item from/to the list | |
public void multi_select(int position) { | |
if (mActionMode != null) { | |
if (multiselect_list.contains(user_list.get(position))) | |
multiselect_list.remove(user_list.get(position)); | |
else | |
multiselect_list.add(user_list.get(position)); | |
if (multiselect_list.size() > 0) | |
mActionMode.setTitle("" + multiselect_list.size()); | |
else | |
mActionMode.setTitle(""); | |
refreshAdapter(); | |
} | |
} | |
@Override | |
public void onBindViewHolder(MyViewHolder holder, int position) { | |
if(selected_usersList.contains(usersList.get(position))) | |
holder.ll_listitem.setBackgroundColor(ContextCompat.getColor(mContext, R.color.list_item_selected_state)); | |
else | |
holder.ll_listitem.setBackgroundColor(ContextCompat.getColor(mContext, R.color.list_item_normal_state)); | |
} | |
if(multiselect_list.size()>0) | |
{ | |
for(int i=0;i<multiselect_list.size();i++) | |
user_list.remove(multiselect_list.get(i)); | |
multiSelectAdapter.notifyDataSetChanged(); | |
if (mActionMode != null) | |
mActionMode.finish(); | |
Toast.makeText(getApplicationContext(), "Delete Click", Toast.LENGTH_SHORT).show(); | |
} | |
public void refreshAdapter() | |
{ | |
multiSelectAdapter.selected_usersList=multiselect_list; | |
multiSelectAdapter.usersList=user_list; | |
multiSelectAdapter.notifyDataSetChanged(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public void initSearchView() | |
{ | |
final SearchView searchView = | |
(SearchView) search_menu.findItem(R.id.action_filter_search).getActionView(); | |
// Enable Submit button | |
searchView.setSubmitButtonEnabled(true); | |
// Change search close button image | |
ImageView closeButton = (ImageView) searchView.findViewById(R.id.search_close_btn); | |
closeButton.setImageResource(R.drawable.ic_close); | |
// Set hint and the text colors | |
EditText txtSearch = ((EditText) searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text)); | |
txtSearch.setHint("Search.."); | |
txtSearch.setHintTextColor(Color.DKGRAY); | |
txtSearch.setTextColor(getResources().getColor(R.color.colorPrimary)); | |
// Set the cursor | |
AutoCompleteTextView searchTextView = (AutoCompleteTextView) searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text); | |
try { | |
Field mCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes"); | |
mCursorDrawableRes.setAccessible(true); | |
mCursorDrawableRes.set(searchTextView, R.drawable.search_cursor); //This sets the cursor resource ID to 0 or @null which will make it visible on white background | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { | |
@Override | |
public boolean onQueryTextSubmit(String query) { | |
callSearch(query); | |
searchView.clearFocus(); | |
return true; | |
} | |
@Override | |
public boolean onQueryTextChange(String newText) { | |
callSearch(newText); | |
return true; | |
} | |
public void callSearch(String query) { | |
//Do searching | |
Log.i("query", "" + query); | |
} | |
}); | |
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) | |
public void circleReveal(int viewID, int posFromRight, boolean containsOverflow, final boolean isShow) | |
{ | |
final View myView = findViewById(viewID); | |
int width=myView.getWidth(); | |
if(posFromRight>0) | |
width<span class="pl-k">-=</span>(posFromRight<span class="pl-k">*</span>getResources()<span class="pl-k">.</span>getDimensionPixelSize(<span class="pl-smi">R</span><span class="pl-k">.</span>dimen<span class="pl-k">.</span>abc_action_button_min_width_material))<span class="pl-k">-</span>(getResources()<span class="pl-k">.</span>getDimensionPixelSize(<span class="pl-smi">R</span><span class="pl-k">.</span>dimen<span class="pl-k">.</span>abc_action_button_min_width_material)<span class="pl-k">/</span> <span class="pl-c1">2</span>); | |
if(containsOverflow) | |
width-=getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_overflow_material); | |
int cx=width; | |
int cy=myView.getHeight()/2; | |
Animator anim; | |
if(isShow) | |
anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0,(float)width); | |
else | |
anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, (float)width, 0); | |
anim.setDuration((long)220); | |
// make the view invisible when the animation is done | |
anim.addListener(new AnimatorListenerAdapter() { | |
@Override | |
public void onAnimationEnd(Animator animation) { | |
if(!isShow) | |
{ | |
super.onAnimationEnd(animation); | |
myView.setVisibility(View.INVISIBLE); | |
} | |
} | |
}); | |
// make the view visible and start the animation | |
if(isShow) | |
myView.setVisibility(View.VISIBLE); | |
// start the animation | |
anim.start(); | |
} | |
// To reveal a previously invisible view | |
circleReveal(R.id.searchtoolbar,1,true,true); | |
// To hide a previously visible view | |
circleReveal(R.id.searchtoolbar,1,true,false); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
dependencies { | |
implementation 'com.android.support:appcompat-v7:27.1.0' | |
implementation 'com.android.support:design:27.1.0' | |
} | |
<?xml version="1.0" encoding="utf-8"?> | |
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<RelativeLayout | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<android.support.design.widget.TabLayout | |
android:id="@+id/tablayout" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:background="@color/colorPrimary" | |
app:tabGravity="fill" | |
app:tabIndicatorColor="@color/colorAccent" | |
app:tabMode="fixed" | |
app:tabSelectedTextColor="@color/colorTextPrimary" | |
app:tabTextColor="@color/colorTextDisable" /> | |
<android.support.v4.view.ViewPager | |
android:id="@+id/viewpager" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:layout_below="@+id/tablayout" | |
app:layout_behavior="@string/appbar_scrolling_view_behavior" /> | |
</RelativeLayout> | |
</android.support.design.widget.CoordinatorLayout> | |
viewPager = (ViewPager) findViewById(R.id.viewpager); | |
tabLayout = (TabLayout) findViewById(R.id.tablayout); | |
tabLayout.setupWithViewPager(viewPager); | |
<android.support.design.widget.TabLayout> | |
<android.support.design.widget.TabItem | |
android:id="@+id/tab1" | |
android:icon="@drawable/tab_call_selector" | |
style="@style/tab_item" | |
android:text="@string/tab1" /> | |
</android.support.design.widget.TabLayout> | |
private void setupTabIcons() | |
{ | |
for(int i=0;i<tabItems.length;i++) | |
{ | |
TabLayout.Tab tabitem = tabLayout.newTab(); | |
tabitem.setCustomView(customView.get(i)); | |
tabLayout.addTab(tabitem); | |
} | |
} | |
<?xml version="1.0" encoding="utf-8"?> | |
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > | |
<item xmlns:android="http://schemas.android.com/apk/res/android"> | |
<shape android:shape="oval"> | |
<solid android:color="@drawable/tab_text_color_selector" /> | |
</shape> | |
</item> | |
</layer-list> | |
<?xml version="1.0" encoding="utf-8"?> | |
<selector xmlns:android="http://schemas.android.com/apk/res/android"> | |
<item android:color="@color/colorTextPrimary" android:state_selected="true" /> | |
<item android:color="@color/colorAccent"/> | |
</selector> | |
<?xml version="1.0" encoding="utf-8"?> | |
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
android:layout_width="match_parent" | |
android:layout_height="48dp" | |
android:padding="12dp"> | |
<LinearLayout | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_centerInParent="true" | |
android:orientation="horizontal"> | |
<TextView | |
android:id="@+id/tv_title" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="Calls" | |
android:textColor="@drawable/tab_text_color_selector" | |
android:textSize="@dimen/large_text" /> | |
<TextView | |
android:id="@+id/tv_count" | |
android:layout_width="20dp" | |
android:layout_height="20dp" | |
android:layout_marginLeft="6dp" | |
android:background="@drawable/badge_background" | |
android:gravity="center" | |
android:text="99" | |
android:textColor="@color/colorPrimary" | |
android:textSize="@dimen/medium_text" /> | |
</LinearLayout> | |
</RelativeLayout> | |
//Initializing viewPager | |
viewPager = (ViewPager) findViewById(R.id.viewpager); | |
viewPager.setOffscreenPageLimit(3); | |
setupViewPager(viewPager); | |
//Initializing the tablayout | |
tabLayout = (TabLayout) findViewById(R.id.tablayout); | |
tabLayout.setupWithViewPager(viewPager); | |
private void setupViewPager(ViewPager viewPager) | |
{ | |
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); | |
callsFragment=new CallsFragment(); | |
chatFragment=new ChatFragment(); | |
contactsFragment=new ContactsFragment(); | |
adapter.addFragment(callsFragment,"CALLS"); | |
adapter.addFragment(chatFragment,"CHAT"); | |
adapter.addFragment(contactsFragment,"CONTACTS"); | |
viewPager.setAdapter(adapter); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment