first version
This commit is contained in:
parent
7399a76899
commit
ea07e8bca2
|
|
@ -25,3 +25,9 @@ proguard/
|
|||
|
||||
# Log Files
|
||||
*.log
|
||||
|
||||
.gradle
|
||||
.idea
|
||||
.DS_Store
|
||||
/captures
|
||||
*.iml
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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 *;
|
||||
#}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<manifest package="org.roboswag.components"/>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<declare-styleable name="TypefacedTextView">
|
||||
<attr name="customTypeface" format="string"/>
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
Loading…
Reference in New Issue