first version

This commit is contained in:
Vsevolod Ivanov 2015-11-10 21:39:00 +03:00
parent 7399a76899
commit ea07e8bca2
15 changed files with 1124 additions and 0 deletions

6
.gitignore vendored
View File

@ -25,3 +25,9 @@ proguard/
# Log Files
*.log
.gradle
.idea
.DS_Store
/captures
*.iml

34
build.gradle Normal file
View File

@ -0,0 +1,34 @@
apply plugin: 'com.android.library'
apply plugin: 'me.tatarka.retrolambda'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
minSdkVersion 9
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.facebook.fresco:fbcore:0.7.0'
compile 'io.reactivex:rxandroid:1.0.1'
}
apply from: "${rootDir}/libraries/BuildScripts/gradle/staticAnalysis.gradle"

17
proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Users\ZuZuK\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@ -0,0 +1 @@
<manifest package="org.roboswag.components"/>

View File

@ -0,0 +1,80 @@
package org.roboswag.components.audio;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import rx.Observable;
import rx.subjects.BehaviorSubject;
/**
* Created by Gavriil Sitnikov on 02/11/2015.
* TODO: fill description
*/
public class HeadsetStateObserver {
@Nullable
private static HeadsetStateObserver instance;
@NonNull
public synchronized static HeadsetStateObserver getInstance(@NonNull Context context) {
if (instance == null) {
instance = new HeadsetStateObserver(context);
}
return instance;
}
private final AudioManager audioManager;
private final BehaviorSubject<Boolean> isPluggedInSubject;
private final Observable<Boolean> isPluggedInObservable;
@Nullable
private IsPluggedInReceiver isPluggedInReceiver;
private HeadsetStateObserver(@NonNull Context context) {
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
isPluggedInSubject = BehaviorSubject.create();
isPluggedInObservable = isPluggedInSubject
.distinctUntilChanged()
.doOnSubscribe(() -> {
isPluggedInReceiver = new IsPluggedInReceiver();
context.registerReceiver(isPluggedInReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
isPluggedInSubject.onNext(isPluggedIn());
})
.doOnUnsubscribe(() -> {
if (isPluggedInReceiver == null) {
throw new IllegalStateException("IsPluggedInReceiver is null on unsubscribe");
}
context.unregisterReceiver(isPluggedInReceiver);
isPluggedInReceiver = null;
})
.replay(1)
.refCount();
}
@SuppressWarnings("deprecation")
public boolean isPluggedIn() {
return audioManager.isWiredHeadsetOn();
}
public Observable<Boolean> observeIsPluggedIn() {
return isPluggedInObservable;
}
private class IsPluggedInReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
isPluggedInSubject.onNext(intent.getExtras().getInt("state") != 0);
}
}
}
}

View File

@ -0,0 +1,205 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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.
*
*/
package org.roboswag.components.audio;
import android.content.Context;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.os.Handler;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.subjects.BehaviorSubject;
/**
* Created by Gavriil Sitnikov on 02/11/2015.
* TODO: fill description
*/
public class VolumeController {
@Nullable
private static VolumeController instance;
@NonNull
public synchronized static VolumeController getInstance(@NonNull Context context) {
if (instance == null) {
instance = new VolumeController(context);
}
return instance;
}
private final AudioManager audioManager;
private final int maxVolume;
private final BehaviorSubject<Integer> volumeSubject;
private final Observable<Integer> volumeObservable;
@Nullable
private VolumeObserver volumeObserver;
@Nullable
private SeekBar seekBar;
@Nullable
private ImageView volumeDown;
@Nullable
private ImageView volumeUp;
@Nullable
private Subscription seekBarSubscription;
private VolumeController(@NonNull Context context) {
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
volumeSubject = BehaviorSubject.create();
volumeObservable = volumeSubject
.distinctUntilChanged()
.doOnSubscribe(() -> {
volumeObserver = new VolumeObserver();
context.getContentResolver()
.registerContentObserver(Settings.System.CONTENT_URI, true, volumeObserver);
updateVolume();
})
.doOnUnsubscribe(() -> {
if (volumeObserver == null) {
throw new IllegalStateException("VolumeObserver is null on unsubscribe");
}
context.getContentResolver()
.unregisterContentObserver(volumeObserver);
volumeObserver = null;
})
.replay(1)
.refCount();
}
private void updateVolume() {
volumeSubject.onNext(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
}
public void setVolume(int value) {
if (value < 0 || value > maxVolume) {
throw new IllegalStateException("Volume: " + value + " out of bounds [0," + maxVolume + "]");
}
if (getVolume() != value) {
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, value, 0);
}
}
public int getVolume() {
return audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
}
@NonNull
public Observable<Integer> observeVolume() {
return volumeObservable;
}
public void attachSeekBar(@NonNull SeekBar seekBar) {
if (this.seekBar != null) {
throw new IllegalArgumentException("Attached SeekBar is not null");
}
this.seekBar = seekBar;
this.seekBar.setMax(maxVolume);
this.seekBar.setProgress(getVolume());
this.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
setVolume(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
seekBarSubscription = observeVolume()
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(seekBar::setProgress);
}
public void attachVolumeButtons(@NonNull ImageView volumeDown, @NonNull ImageView volumeUp) {
if (this.volumeDown != null && this.volumeUp != null) {
throw new IllegalArgumentException("Attached volume buttons is not null");
}
this.volumeDown = volumeDown;
this.volumeUp = volumeUp;
volumeUp.setOnClickListener(v -> {
if (getVolume() != maxVolume) {
setVolume(getVolume() + 1);
}
});
volumeDown.setOnClickListener(v -> {
if (getVolume() != 0) {
setVolume(getVolume() - 1);
}
});
seekBarSubscription = observeVolume()
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(seekBar::setProgress);
}
public void detachSeekBar(@NonNull SeekBar seekBar) {
if (this.seekBar != seekBar) {
throw new IllegalArgumentException("Wrong SeekBar: " + seekBar + " != " + this.seekBar);
}
if (seekBarSubscription == null) {
throw new IllegalStateException("SeekBarSubscription is null on detach of SeekBar");
}
this.seekBar.setOnSeekBarChangeListener(null);
seekBarSubscription.unsubscribe();
seekBarSubscription = null;
this.seekBar = null;
}
public void detachVolumeButtons(@NonNull ImageView volumeDownImageView, @NonNull ImageView volumeUpImageView) {
if (this.volumeDown != volumeDownImageView && this.volumeUp != volumeUpImageView) {
throw new IllegalArgumentException("Wrong SeekBar: " + seekBar + " != " + this.seekBar);
}
this.volumeDown = null;
this.volumeUp = null;
}
private class VolumeObserver extends ContentObserver {
public VolumeObserver() {
super(new Handler());
}
@Override
public void onChange(boolean selfChange) {
updateVolume();
}
}
}

View File

@ -0,0 +1,234 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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.
*
*/
package org.roboswag.components.navigation;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
/**
* Created by Gavriil Sitnikov on 21/10/2015.
* TODO: fill description
*/
public abstract class BaseActivity extends AppCompatActivity
implements FragmentManager.OnBackStackChangedListener,
OnFragmentStartedListener {
private static final String TOP_FRAGMENT_TAG_MARK = "TOP_FRAGMENT";
private boolean isPaused = false;
/* Returns id of main fragments container where navigation-node fragments should be */
protected int getFragmentContainerId() {
throw new UnsupportedOperationException("Implement getFragmentContainerId method to use fragment managing");
}
/* Returns if last fragment in stack is top (added by setFragment) like fragment from sidebar menu */
public boolean isCurrentFragmentTop() {
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() == 0) {
return true;
}
String topFragmentTag = fragmentManager
.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1)
.getName();
return topFragmentTag != null && topFragmentTag.contains(TOP_FRAGMENT_TAG_MARK);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportFragmentManager().addOnBackStackChangedListener(this);
}
@Override
protected void onResume() {
isPaused = false;
super.onResume();
}
@Override
protected void onPause() {
isPaused = true;
super.onPause();
}
@Override
public void onFragmentStarted(BaseFragment fragment) {
}
/* Raises when back stack changes */
@Override
public void onBackStackChanged() {
}
/* Setting fragment of special class as first in stack */
public Fragment setFirstFragment(Class<?> fragmentClass) {
return setFirstFragment(fragmentClass, null);
}
/* Setting fragment of special class as first in stack with args */
public Fragment setFirstFragment(Class<?> fragmentClass, Bundle args) {
if (isPaused) {
//TODO: log
return null;
}
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
Fragment fragment = Fragment.instantiate(this, fragmentClass.getName(), args);
fragmentManager.beginTransaction()
.replace(getFragmentContainerId(), fragment, null)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit();
return fragment;
}
private Fragment addFragmentToStack(Class<?> fragmentClass, Bundle args, String backStackTag) {
if (isPaused) {
//TODO: log
return null;
}
Fragment fragment = Fragment.instantiate(this, fragmentClass.getName(), args);
getSupportFragmentManager().beginTransaction()
.replace(getFragmentContainerId(), fragment, backStackTag)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(backStackTag)
.commit();
return fragment;
}
/* Setting fragment of special class as top */
public Fragment setFragment(Class fragmentClass) {
return setFragment(fragmentClass, null);
}
/* Setting fragment of special class as top with args */
public Fragment setFragment(Class fragmentClass, Bundle args) {
return addFragmentToStack(fragmentClass, args, fragmentClass.getName() + ' ' + TOP_FRAGMENT_TAG_MARK);
}
/* Pushing fragment of special class to fragments stack */
public Fragment pushFragment(Class fragmentClass) {
return pushFragment(fragmentClass, null);
}
/* Pushing fragment of special class with args to fragments stack */
public Fragment pushFragment(Class fragmentClass, Bundle args) {
return addFragmentToStack(fragmentClass, args, fragmentClass.getName());
}
/* Raises when device back button pressed */
@Override
public void onBackPressed() {
FragmentManager fragmentManager = getSupportFragmentManager();
boolean backPressResult = false;
if (fragmentManager.getFragments() != null) {
for (Fragment fragment : fragmentManager.getFragments()) {
if (fragment != null && fragment.isResumed() && fragment instanceof BaseFragment) {
backPressResult = backPressResult || ((BaseFragment) fragment).onBackPressed();
}
}
}
if (!backPressResult) {
super.onBackPressed();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
FragmentManager fragmentManager = getSupportFragmentManager();
boolean homePressResult = false;
if (fragmentManager.getFragments() != null) {
for (Fragment fragment : fragmentManager.getFragments()) {
if (fragment != null
&& fragment.isResumed()
&& fragment instanceof BaseFragment) {
homePressResult = homePressResult || ((BaseFragment) fragment).onHomePressed();
}
}
}
if (homePressResult) {
return true;
}
int stackSize = fragmentManager.getBackStackEntryCount();
switch (stackSize) {
case 0:
return false;
case 1:
fragmentManager.popBackStack();
return true;
default:
String lastFragmentName = fragmentManager.getBackStackEntryAt(stackSize - 1).getName();
for (int i = stackSize - 2; i >= 0; i--) {
String currentFragmentName = fragmentManager.getBackStackEntryAt(i).getName();
if (currentFragmentName == null || !currentFragmentName.equals(lastFragmentName)) {
fragmentManager.popBackStackImmediate(currentFragmentName, 0);
break;
} else if (i == 0) {
fragmentManager.popBackStackImmediate(currentFragmentName, FragmentManager.POP_BACK_STACK_INCLUSIVE);
} else {
lastFragmentName = currentFragmentName;
}
}
return true;
}
}
return super.onOptionsItemSelected(item);
}
/* Hides device keyboard */
public void hideSoftInput() {
if (getCurrentFocus() != null) {
InputMethodManager inputManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
View mainFragmentContainer = findViewById(getFragmentContainerId());
if (mainFragmentContainer != null) {
mainFragmentContainer.requestFocus();
}
}
}
/* Shows device keyboard */
public void showSoftInput(View view) {
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
}

View File

@ -0,0 +1,207 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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.
*
*/
package org.roboswag.components.navigation;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.view.View;
/**
* Created by Gavriil Sitnikov on 21/10/2015.
* TODO: fill description
*/
public abstract class BaseFragment<TViewHolder extends BaseFragment.ViewHolder> extends Fragment
implements OnFragmentStartedListener {
@Nullable
private TViewHolder viewHolder;
/* Returns base activity */
@Nullable
protected BaseActivity getBaseActivity() {
return (BaseActivity) getActivity();
}
@Nullable
protected TViewHolder getViewHolder() {
return viewHolder;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (view == null) {
throw new IllegalStateException("Background fragments are deprecated - view shouldn't be null");
}
viewHolder = createViewHolder(view, savedInstanceState);
}
@NonNull
protected abstract TViewHolder createViewHolder(@NonNull View view, @Nullable Bundle savedInstanceState);
@Override
public void onFragmentStarted(BaseFragment fragment) {
}
@Deprecated
@Override
public void onStart() {
super.onStart();
if (viewHolder == null || getBaseActivity() == null) {
throw new IllegalStateException("ViewHolder or BaseActivity is null at onStart point");
}
onStart(viewHolder, getBaseActivity());
}
protected void onStart(@NonNull TViewHolder viewHolder, @NonNull BaseActivity baseActivity) {
Fragment parentFragment = getParentFragment();
if (parentFragment != null) {
if (parentFragment instanceof OnFragmentStartedListener) {
((OnFragmentStartedListener) parentFragment).onFragmentStarted(this);
}
} else {
baseActivity.onFragmentStarted(this);
}
}
@Deprecated
@Override
public void onResume() {
super.onResume();
if (viewHolder == null || getBaseActivity() == null) {
throw new IllegalStateException("ViewHolder or BaseActivity is null at onResume point");
}
onResume(viewHolder, getBaseActivity());
}
protected void onResume(@NonNull TViewHolder viewHolder, @NonNull BaseActivity baseActivity) {
}
/* Raises when device back button pressed */
public boolean onBackPressed() {
FragmentManager fragmentManager = getChildFragmentManager();
boolean result = false;
if (fragmentManager.getFragments() == null) {
return false;
}
for (Fragment fragment : fragmentManager.getFragments()) {
if (fragment != null
&& fragment.isResumed()
&& fragment instanceof BaseFragment) {
result = result || ((BaseFragment) fragment).onBackPressed();
}
}
return result;
}
/* Raises when ActionBar home button pressed */
public boolean onHomePressed() {
FragmentManager fragmentManager = getChildFragmentManager();
boolean result = false;
if (fragmentManager.getFragments() == null) {
return false;
}
for (Fragment fragment : fragmentManager.getFragments()) {
if (fragment != null
&& fragment.isResumed()
&& fragment instanceof BaseFragment) {
result = result || ((BaseFragment) fragment).onHomePressed();
}
}
return result;
}
@Deprecated
@Override
public void onPause() {
if (viewHolder == null || getBaseActivity() == null) {
throw new IllegalStateException("ViewHolder or BaseActivity is null at onPause point");
}
onPause(viewHolder, getBaseActivity());
super.onPause();
}
protected void onPause(@NonNull TViewHolder viewHolder, @NonNull BaseActivity baseActivity) {
}
@Deprecated
@Override
public void onStop() {
if (viewHolder == null || getBaseActivity() == null) {
throw new IllegalStateException("ViewHolder or BaseActivity is null at onStop point");
}
onStop(viewHolder, getBaseActivity());
super.onStop();
}
protected void onStop(@NonNull TViewHolder viewHolder, @NonNull BaseActivity baseActivity) {
}
@Deprecated
@Override
public void onDestroyView() {
if (viewHolder == null) {
throw new IllegalStateException("ViewHolder is null at onStop point");
}
onDestroyView(viewHolder);
super.onDestroyView();
this.viewHolder = null;
}
protected void onDestroyView(@NonNull TViewHolder viewHolder) {
viewHolder.onDestroy();
}
public class ViewHolder {
private final Context context;
private final Handler postHandler = new Handler();
/* Returns post handler to executes code on UI thread */
@NonNull
protected Handler getPostHandler() {
return postHandler;
}
@NonNull
protected Context getContext() {
return context;
}
public ViewHolder(@NonNull View view){
context = view.getContext();
}
protected void onDestroy() {
postHandler.removeCallbacksAndMessages(null);
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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.
*
*/
package org.roboswag.components.navigation;
/**
* Created by Gavriil Sitnikov on 08/10/2014.
* Base interface to listen fragment changing
*/
public interface OnFragmentStartedListener {
/* Raises by fragment to notify that it is started */
void onFragmentStarted(BaseFragment fragment);
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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.
*
*/
package org.roboswag.components.services;
import android.app.Service;
import android.os.Binder;
import android.support.annotation.NonNull;
/**
* Created by Gavriil Sitnikov on 03/10/2015.
* Basic binding to service
*/
public class ServiceBinder<TService extends Service> extends Binder {
@NonNull
private final TService service;
public ServiceBinder(@NonNull TService service) {
this.service = service;
}
/* Returns service that binder holds */
@NonNull
public TService getService() {
return service;
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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.
*
*/
package org.roboswag.components.telephony;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import rx.Observable;
import rx.subjects.BehaviorSubject;
/**
* Created by Gavriil Sitnikov on 02/11/2015.
* TODO: fill description
*/
public class IsCallingObserver {
@Nullable
private static IsCallingObserver instance;
@NonNull
public synchronized static IsCallingObserver getInstance(@NonNull Context context) {
if (instance == null) {
instance = new IsCallingObserver(context);
}
return instance;
}
private final BehaviorSubject<Boolean> isCallingSubject = BehaviorSubject.create();
private final Observable<Boolean> isCallingObservable;
private IsCallingObserver(@NonNull Context context) {
TelephonyManager phoneStateManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
phoneStateManager.listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
isCallingSubject.onNext(isCallingState(state));
}
}, PhoneStateListener.LISTEN_CALL_STATE);
isCallingSubject.onNext(isCallingState(phoneStateManager.getCallState()));
isCallingObservable = isCallingSubject
.distinctUntilChanged()
.replay(1)
.refCount();
}
private boolean isCallingState(int state) {
return state != TelephonyManager.CALL_STATE_IDLE;
}
@NonNull
public Observable<Boolean> observeIsCalling() {
return isCallingObservable;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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.
*
*/
package org.roboswag.components.utils;
import android.net.Uri;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import com.facebook.common.util.UriUtil;
/**
* Created by Gavriil Sitnikov on 20/10/2015.
* TODO: fill description
*/
public final class FrescoUtils {
@NonNull
public static Uri getResourceUri(@DrawableRes int resourceId) {
return new Uri.Builder().scheme(UriUtil.LOCAL_RESOURCE_SCHEME).path(String.valueOf(resourceId)).build();
}
private FrescoUtils() {
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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.
*
*/
package org.roboswag.components.utils;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Typeface;
import android.support.annotation.NonNull;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
* Created by Gavriil Sitnikov on 18/07/2014.
* Typefaces manager
*/
public class Typefaces {
private static final HashMap<String, Typeface> TYPEFACES = new HashMap<>();
/**
* Returns typeface by name from assets 'fonts' folder
*/
@NonNull
public synchronized static Typeface getByName(@NonNull Context context, @NonNull String name) {
Typeface result = TYPEFACES.get(name);
if (result == null) {
AssetManager assetManager = context.getAssets();
try {
List<String> fonts = Arrays.asList(assetManager.list("fonts"));
if (fonts.contains(name + ".ttf")) {
result = Typeface.createFromAsset(assetManager, "fonts/" + name + ".ttf");
} else if (fonts.contains(name + ".otf")) {
result = Typeface.createFromAsset(assetManager, "fonts/" + name + ".otf");
} else
throw new IllegalStateException("Can't find .otf or .ttf file in folder 'fonts' with name: " + name);
} catch (IOException e) {
throw new IllegalStateException("Typefaces files should be in folder named 'fonts'");
}
TYPEFACES.put(name, result);
}
return result;
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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.
*
*/
package org.roboswag.components.views;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;
import org.roboswag.components.R;
import org.roboswag.components.utils.Typefaces;
/**
* Created by Gavriil Sitnikov on 18/07/2014.
* TextView that supports fonts from Typefaces class
*/
public class TypefacedTextView extends TextView {
public TypefacedTextView(Context context) {
super(context);
initialize(context, null);
}
public TypefacedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs);
}
public TypefacedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize(context, attrs);
}
public void setTypeface(String name, int style) {
setTypeface(Typefaces.getByName(getContext(), name), style);
}
public void setTypeface(String name) {
setTypeface(name, Typeface.NORMAL);
}
private void initialize(Context context, AttributeSet attrs) {
String customTypeface = null;
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TypefacedTextView);
customTypeface = a.getString(R.styleable.TypefacedTextView_customTypeface);
a.recycle();
}
if (customTypeface != null && !isInEditMode()) {
Typeface typeface = getTypeface();
setTypeface(customTypeface, typeface != null ? typeface.getStyle() : Typeface.NORMAL);
}
}
}

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TypefacedTextView">
<attr name="customTypeface" format="string"/>
</declare-styleable>
</resources>